Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- Biomanus/.env +1 -0
- Biomanus/.env.example +22 -0
- Biomanus/.gitattributes +2 -0
- Biomanus/.gitignore +236 -0
- Biomanus/.pre-commit-config.yaml +41 -0
- Biomanus/3xTG_AD_DE_results.csv +0 -0
- Biomanus/3xTG_AD_counts.csv +0 -0
- Biomanus/3xTG_AD_metadata.csv +7 -0
- Biomanus/5xFAD_DE_results.csv +0 -0
- Biomanus/5xFAD_counts.csv +0 -0
- Biomanus/5xFAD_counts_int.csv +0 -0
- Biomanus/5xFAD_metadata.csv +21 -0
- Biomanus/ALZHEIMER_BENCHMARK_GUIDE.md +261 -0
- Biomanus/BENCHMARK_EXECUTION_SUMMARY.md +183 -0
- Biomanus/LICENSE +201 -0
- Biomanus/LMX1B.meme +19 -0
- Biomanus/MANIFEST.in +27 -0
- Biomanus/README.md +383 -0
- Biomanus/article.xml +1 -0
- Biomanus/biomni.md +558 -0
- Biomanus/biomni/__init__.py +3 -0
- Biomanus/biomni/__pycache__/__init__.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/__init__.cpython-311.pyc +0 -0
- Biomanus/biomni/__pycache__/__init__.cpython-313.pyc +0 -0
- Biomanus/biomni/__pycache__/config.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/config.cpython-311.pyc +0 -0
- Biomanus/biomni/__pycache__/config.cpython-313.pyc +0 -0
- Biomanus/biomni/__pycache__/env_desc.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/env_desc.cpython-311.pyc +0 -0
- Biomanus/biomni/__pycache__/llm.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/llm.cpython-311.pyc +0 -0
- Biomanus/biomni/__pycache__/llm.cpython-313.pyc +0 -0
- Biomanus/biomni/__pycache__/utils.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/utils.cpython-313.pyc +0 -0
- Biomanus/biomni/__pycache__/version.cpython-310.pyc +0 -0
- Biomanus/biomni/__pycache__/version.cpython-311.pyc +0 -0
- Biomanus/biomni/__pycache__/version.cpython-313.pyc +0 -0
- Biomanus/biomni/agent/__init__.py +1 -0
- Biomanus/biomni/agent/__pycache__/__init__.cpython-310.pyc +0 -0
- Biomanus/biomni/agent/__pycache__/__init__.cpython-311.pyc +0 -0
- Biomanus/biomni/agent/__pycache__/__init__.cpython-313.pyc +0 -0
- Biomanus/biomni/agent/__pycache__/a1.cpython-310.pyc +0 -0
- Biomanus/biomni/agent/a1.py +0 -0
- Biomanus/biomni/agent/env_collection.py +313 -0
- Biomanus/biomni/agent/function_generator.py +119 -0
- Biomanus/biomni/agent/qa_llm.py +50 -0
- Biomanus/biomni/agent/react.py +465 -0
- Biomanus/biomni/biorxiv_scripts/extract_biorxiv_tasks.py +404 -0
- Biomanus/biomni/biorxiv_scripts/generate_function.py +72 -0
- Biomanus/biomni/biorxiv_scripts/process_all_subjects.py +315 -0
Biomanus/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export DEEPSEEK_API_KEY=sk-06e6154722b84e89b081b1c9571838ef
|
Biomanus/.env.example
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni Environment Configuration
|
| 2 |
+
# Copy this file to .env and fill in your actual API keys
|
| 3 |
+
|
| 4 |
+
# Required: Anthropic API Key for Claude models
|
| 5 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 6 |
+
|
| 7 |
+
# Optional: OpenAI API Key (if using OpenAI models)
|
| 8 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 9 |
+
|
| 10 |
+
# Optional: AWS Bedrock Configuration (if using AWS Bedrock models)
|
| 11 |
+
AWS_BEARER_TOKEN_BEDROCK=your_bedrock_api_key_here
|
| 12 |
+
AWS_REGION=us-east-1
|
| 13 |
+
|
| 14 |
+
# Optional: Custom model serving configuration
|
| 15 |
+
# CUSTOM_MODEL_BASE_URL=http://localhost:8000/v1
|
| 16 |
+
# CUSTOM_MODEL_API_KEY=your_custom_api_key_here
|
| 17 |
+
|
| 18 |
+
# Optional: Biomni data path (defaults to ./data)
|
| 19 |
+
# BIOMNI_DATA_PATH=/path/to/your/data
|
| 20 |
+
|
| 21 |
+
# Optional: Timeout settings
|
| 22 |
+
# BIOMNI_TIMEOUT_SECONDS=600
|
Biomanus/.gitattributes
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.bam filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.h5ad filter=lfs diff=lfs merge=lfs -text
|
Biomanus/.gitignore
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.gitignore
|
| 2 |
+
.buildplan
|
| 3 |
+
.env
|
| 4 |
+
debug*
|
| 5 |
+
.*
|
| 6 |
+
# Byte-compiled / optimized / DLL files
|
| 7 |
+
biomni_release/biomni_env/biomni_tools/
|
| 8 |
+
open_source_process.ipynb
|
| 9 |
+
biomni_env/biomni_tools/*
|
| 10 |
+
.gradio/
|
| 11 |
+
*xlsx
|
| 12 |
+
scripts/*.png
|
| 13 |
+
notebooks/figures/*
|
| 14 |
+
notebooks/tmp_dir/*
|
| 15 |
+
notebooks/tmp_directory/*
|
| 16 |
+
notebooks/save_folder/*
|
| 17 |
+
notebooks/cache
|
| 18 |
+
notebooks/\[draft*
|
| 19 |
+
notebooks/\[scratch*
|
| 20 |
+
notebooks/labbench/*
|
| 21 |
+
scripts/save_folder/*
|
| 22 |
+
scripts/\[draft*
|
| 23 |
+
scripts/tmp_dir/*
|
| 24 |
+
scripts/tmp_directory/*
|
| 25 |
+
scripts/figures/*
|
| 26 |
+
*/scripts/outputs/*
|
| 27 |
+
*/scripts/wandb/*
|
| 28 |
+
*/results/*
|
| 29 |
+
bioagentos/agent/popper_agent.py
|
| 30 |
+
bioagentos/scripts/biorxiv_results
|
| 31 |
+
bioagentos/tool/data_tool/old_data_tool/*
|
| 32 |
+
examples/*.png
|
| 33 |
+
examples/*.csv
|
| 34 |
+
examples/*.fa
|
| 35 |
+
scratch/
|
| 36 |
+
packages/
|
| 37 |
+
data
|
| 38 |
+
dataset
|
| 39 |
+
logs
|
| 40 |
+
scripts/gradio
|
| 41 |
+
data_lake
|
| 42 |
+
__pycache__/
|
| 43 |
+
*.py[cod]
|
| 44 |
+
*$py.class
|
| 45 |
+
res/
|
| 46 |
+
examples/tmp*
|
| 47 |
+
bioagentos/task/lab_bench.py
|
| 48 |
+
*database/
|
| 49 |
+
.history/
|
| 50 |
+
|
| 51 |
+
temp.*
|
| 52 |
+
|
| 53 |
+
scripts/analysis/all_tools.txt
|
| 54 |
+
|
| 55 |
+
BIOMNI_GRADIO/biomni/tool/files/*
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
# C extensions
|
| 59 |
+
*.so
|
| 60 |
+
|
| 61 |
+
# Distribution / packaging
|
| 62 |
+
.Python
|
| 63 |
+
build/
|
| 64 |
+
develop-eggs/
|
| 65 |
+
dist/
|
| 66 |
+
downloads/
|
| 67 |
+
eggs/
|
| 68 |
+
.eggs/
|
| 69 |
+
lib/
|
| 70 |
+
lib64/
|
| 71 |
+
parts/
|
| 72 |
+
sdist/
|
| 73 |
+
var/
|
| 74 |
+
wheels/
|
| 75 |
+
share/python-wheels/
|
| 76 |
+
*.egg-info/
|
| 77 |
+
.installed.cfg
|
| 78 |
+
*.egg
|
| 79 |
+
MANIFEST
|
| 80 |
+
|
| 81 |
+
# PyInstaller
|
| 82 |
+
# Usually these files are written by a python script from a template
|
| 83 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 84 |
+
*.manifest
|
| 85 |
+
*.spec
|
| 86 |
+
|
| 87 |
+
# Installer logs
|
| 88 |
+
pip-log.txt
|
| 89 |
+
pip-delete-this-directory.txt
|
| 90 |
+
|
| 91 |
+
# Unit test / coverage reports
|
| 92 |
+
htmlcov/
|
| 93 |
+
.tox/
|
| 94 |
+
.nox/
|
| 95 |
+
.coverage
|
| 96 |
+
.coverage.*
|
| 97 |
+
.cache
|
| 98 |
+
nosetests.xml
|
| 99 |
+
coverage.xml
|
| 100 |
+
*.cover
|
| 101 |
+
*.py,cover
|
| 102 |
+
.hypothesis/
|
| 103 |
+
.pytest_cache/
|
| 104 |
+
cover/
|
| 105 |
+
test.ipynb
|
| 106 |
+
|
| 107 |
+
# Translations
|
| 108 |
+
*.mo
|
| 109 |
+
*.pot
|
| 110 |
+
|
| 111 |
+
# Django stuff:
|
| 112 |
+
*.log
|
| 113 |
+
local_settings.py
|
| 114 |
+
db.sqlite3
|
| 115 |
+
db.sqlite3-journal
|
| 116 |
+
|
| 117 |
+
# Flask stuff:
|
| 118 |
+
instance/
|
| 119 |
+
.webassets-cache
|
| 120 |
+
|
| 121 |
+
# Scrapy stuff:
|
| 122 |
+
.scrapy
|
| 123 |
+
|
| 124 |
+
# Sphinx documentation
|
| 125 |
+
docs/_build/
|
| 126 |
+
|
| 127 |
+
# PyBuilder
|
| 128 |
+
.pybuilder/
|
| 129 |
+
target/
|
| 130 |
+
|
| 131 |
+
# Jupyter Notebook
|
| 132 |
+
.ipynb_checkpoints
|
| 133 |
+
|
| 134 |
+
# IPython
|
| 135 |
+
profile_default/
|
| 136 |
+
ipython_config.py
|
| 137 |
+
|
| 138 |
+
# pyenv
|
| 139 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 140 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 141 |
+
# .python-version
|
| 142 |
+
|
| 143 |
+
# pipenv
|
| 144 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 145 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 146 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 147 |
+
# install all needed dependencies.
|
| 148 |
+
#Pipfile.lock
|
| 149 |
+
|
| 150 |
+
# poetry
|
| 151 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 152 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 153 |
+
# commonly ignored for libraries.
|
| 154 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 155 |
+
#poetry.lock
|
| 156 |
+
|
| 157 |
+
# pdm
|
| 158 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 159 |
+
#pdm.lock
|
| 160 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
| 161 |
+
# in version control.
|
| 162 |
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
| 163 |
+
.pdm.toml
|
| 164 |
+
.pdm-python
|
| 165 |
+
.pdm-build/
|
| 166 |
+
|
| 167 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 168 |
+
__pypackages__/
|
| 169 |
+
|
| 170 |
+
# Celery stuff
|
| 171 |
+
celerybeat-schedule
|
| 172 |
+
celerybeat.pid
|
| 173 |
+
|
| 174 |
+
# SageMath parsed files
|
| 175 |
+
*.sage.py
|
| 176 |
+
|
| 177 |
+
# Environments
|
| 178 |
+
.env
|
| 179 |
+
.venv
|
| 180 |
+
env/
|
| 181 |
+
venv/
|
| 182 |
+
ENV/
|
| 183 |
+
env.bak/
|
| 184 |
+
venv.bak/
|
| 185 |
+
.envrc
|
| 186 |
+
uv.lock
|
| 187 |
+
|
| 188 |
+
# Spyder project settings
|
| 189 |
+
.spyderproject
|
| 190 |
+
.spyproject
|
| 191 |
+
|
| 192 |
+
# Rope project settings
|
| 193 |
+
.ropeproject
|
| 194 |
+
|
| 195 |
+
# mkdocs documentation
|
| 196 |
+
/site
|
| 197 |
+
|
| 198 |
+
# mypy
|
| 199 |
+
.mypy_cache/
|
| 200 |
+
.dmypy.json
|
| 201 |
+
dmypy.json
|
| 202 |
+
|
| 203 |
+
*.jsonl
|
| 204 |
+
|
| 205 |
+
# Pyre type checker
|
| 206 |
+
.pyre/
|
| 207 |
+
|
| 208 |
+
# pytype static type analyzer
|
| 209 |
+
.pytype/
|
| 210 |
+
|
| 211 |
+
# Cython debug symbols
|
| 212 |
+
cython_debug/
|
| 213 |
+
|
| 214 |
+
# macOS system files
|
| 215 |
+
.DS_Store
|
| 216 |
+
**/.DS_Store
|
| 217 |
+
|
| 218 |
+
# DDInter data files
|
| 219 |
+
biomni/tool/schema_db/ddinter_*.pkl
|
| 220 |
+
data/ddinter_raw/
|
| 221 |
+
|
| 222 |
+
# PyCharm
|
| 223 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 224 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 225 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 226 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 227 |
+
#.idea/
|
| 228 |
+
|
| 229 |
+
# VSCode
|
| 230 |
+
/.vscode
|
| 231 |
+
|
| 232 |
+
# Sphinx build
|
| 233 |
+
/docs/build/
|
| 234 |
+
/docs/source/api/
|
| 235 |
+
/paper_reproduction_runs/
|
| 236 |
+
/bioagent-bench-runs/
|
Biomanus/.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fail_fast: false
|
| 2 |
+
default_language_version:
|
| 3 |
+
python: python3
|
| 4 |
+
default_stages:
|
| 5 |
+
- pre-commit
|
| 6 |
+
- pre-push
|
| 7 |
+
minimum_pre_commit_version: 2.16.0
|
| 8 |
+
repos:
|
| 9 |
+
- repo: https://github.com/biomejs/pre-commit
|
| 10 |
+
rev: v2.3.10
|
| 11 |
+
hooks:
|
| 12 |
+
- id: biome-format
|
| 13 |
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
| 14 |
+
rev: v0.14.11
|
| 15 |
+
hooks:
|
| 16 |
+
- id: ruff-check
|
| 17 |
+
args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes]
|
| 18 |
+
- id: ruff-format
|
| 19 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 20 |
+
rev: v6.0.0
|
| 21 |
+
hooks:
|
| 22 |
+
- id: detect-private-key
|
| 23 |
+
- id: check-ast
|
| 24 |
+
- id: end-of-file-fixer
|
| 25 |
+
- id: mixed-line-ending
|
| 26 |
+
args: [--fix=lf]
|
| 27 |
+
- id: trailing-whitespace
|
| 28 |
+
- id: check-case-conflict
|
| 29 |
+
- id: check-added-large-files
|
| 30 |
+
- id: check-toml
|
| 31 |
+
- id: check-yaml
|
| 32 |
+
- id: check-merge-conflict
|
| 33 |
+
- id: no-commit-to-branch
|
| 34 |
+
args: ["--branch=main"]
|
| 35 |
+
#- repo: https://github.com/pre-commit/mirrors-mypy
|
| 36 |
+
# rev: v1.16.1
|
| 37 |
+
# hooks:
|
| 38 |
+
# - id: mypy
|
| 39 |
+
# args: [--no-strict-optional, --ignore-missing-imports]
|
| 40 |
+
# additional_dependencies:
|
| 41 |
+
# ["types-setuptools", "types-requests", "types-attrs"]
|
Biomanus/3xTG_AD_DE_results.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/3xTG_AD_counts.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/3xTG_AD_metadata.csv
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sample,condition
|
| 2 |
+
G3R1_Cortex_3xTgAD,3xTG_AD
|
| 3 |
+
G3R3_Cortex_3xTgAD,3xTG_AD
|
| 4 |
+
G3R4_Cortex_3xTgAD,3xTG_AD
|
| 5 |
+
G3R10_Cortex_WT,WT
|
| 6 |
+
G3R7_Cortex_WT,WT
|
| 7 |
+
G3R9_Cortex_WT,WT
|
Biomanus/5xFAD_DE_results.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/5xFAD_counts.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/5xFAD_counts_int.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/5xFAD_metadata.csv
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sample,condition
|
| 2 |
+
5xFAD;BL6_cortex_8mon_Female_295,5xFAD
|
| 3 |
+
5xFAD;BL6_cortex_8mon_Female_312,5xFAD
|
| 4 |
+
5xFAD;BL6_cortex_8mon_Female_339,5xFAD
|
| 5 |
+
5xFAD;BL6_cortex_8mon_Female_341,5xFAD
|
| 6 |
+
5xFAD;BL6_cortex_8mon_Female_342,5xFAD
|
| 7 |
+
5xFAD;BL6_cortex_8mon_Male_299,5xFAD
|
| 8 |
+
5xFAD;BL6_cortex_8mon_Male_300,5xFAD
|
| 9 |
+
5xFAD;BL6_cortex_8mon_Male_307,5xFAD
|
| 10 |
+
5xFAD;BL6_cortex_8mon_Male_387,5xFAD
|
| 11 |
+
5xFAD;BL6_cortex_8mon_Male_390,5xFAD
|
| 12 |
+
BL6_cortex_8mon_Female_322,WT
|
| 13 |
+
BL6_cortex_8mon_Female_338,WT
|
| 14 |
+
BL6_cortex_8mon_Female_340,WT
|
| 15 |
+
BL6_cortex_8mon_Female_348,WT
|
| 16 |
+
BL6_cortex_8mon_Female_351,WT
|
| 17 |
+
BL6_cortex_8mon_Male_389,WT
|
| 18 |
+
BL6_cortex_8mon_Male_396,WT
|
| 19 |
+
BL6_cortex_8mon_Male_399,WT
|
| 20 |
+
BL6_cortex_8mon_Male_410,WT
|
| 21 |
+
BL6_cortex_8mon_Male_412,WT
|
Biomanus/ALZHEIMER_BENCHMARK_GUIDE.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Alzheimer's Disease Mouse Models Benchmark 执行指南
|
| 2 |
+
|
| 3 |
+
本文档详细说明如何使用Hypo_Bio_OS agent系统运行Alzheimer's Disease mouse models benchmark分析。
|
| 4 |
+
|
| 5 |
+
## 概述
|
| 6 |
+
|
| 7 |
+
本benchmark要求对三种不同的Alzheimer's Disease小鼠模型(5xFAD、3xTG-AD和PS3O1S)进行比较分析,识别共享的分子通路。分析包括:
|
| 8 |
+
1. 差异表达分析
|
| 9 |
+
2. 使用KEGG通路进行通路富集分析
|
| 10 |
+
3. 生成包含三个模型共有通路及对应p-value的比较表格
|
| 11 |
+
|
| 12 |
+
## 文件结构
|
| 13 |
+
|
| 14 |
+
### 数据文件
|
| 15 |
+
```
|
| 16 |
+
/225040511/project/bioagent-bench/dataset/alzheimer-mouse/data/
|
| 17 |
+
├── GSE168137_countList.txt # 5xFAD模型数据
|
| 18 |
+
├── GSE161904_Raw_gene_counts_cortex.txt # 3xTG-AD模型数据
|
| 19 |
+
└── DEA_PS3O1S.csv # PS3O1S模型差异表达结果
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
### 生成的脚本
|
| 23 |
+
```
|
| 24 |
+
/225040511/project/Hypo_Bio_OS/
|
| 25 |
+
├── run_alzheimer_benchmark.py # 主执行脚本
|
| 26 |
+
├── test_alzheimer_analysis.py # 测试脚本
|
| 27 |
+
├── verify_data_access.py # 数据访问验证脚本
|
| 28 |
+
└── ALZHEIMER_BENCHMARK_GUIDE.md # 本指南
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
### 结果目录
|
| 32 |
+
```
|
| 33 |
+
/225040511/project/Hypo_Bio_OS/bioagent-bench-results/
|
| 34 |
+
├── README.md # 结果说明
|
| 35 |
+
├── template/ # 目录结构模板
|
| 36 |
+
└── alzheimer_analysis_YYYYMMDD_HHMMSS/ # 每次运行的结果
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
## 环境要求
|
| 40 |
+
|
| 41 |
+
### 1. Python环境
|
| 42 |
+
- Python 3.8+
|
| 43 |
+
- 已安装Hypo_Bio_OS依赖
|
| 44 |
+
- 建议使用conda环境 `biomni_e1`
|
| 45 |
+
|
| 46 |
+
### 2. 生物信息学工具
|
| 47 |
+
- R语言环境(用于部分分析)
|
| 48 |
+
- 必要的R包:DESeq2, clusterProfiler, org.Mm.eg.db等
|
| 49 |
+
- Python生物信息学库:scanpy, mygene, gseapy等
|
| 50 |
+
|
| 51 |
+
### 3. API密钥
|
| 52 |
+
- DeepSeek API密钥(或其他支持的LLM API密钥)
|
| 53 |
+
- 设置环境变量:`export DEEPSEEK_API_KEY='your_api_key'`
|
| 54 |
+
|
| 55 |
+
## 执行步骤
|
| 56 |
+
|
| 57 |
+
### 步骤1:验证数据访问
|
| 58 |
+
```bash
|
| 59 |
+
cd /225040511/project/Hypo_Bio_OS
|
| 60 |
+
python verify_data_access.py
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
### 步骤2:设置API密钥
|
| 64 |
+
```bash
|
| 65 |
+
# 方法1:设置环境变量
|
| 66 |
+
export DEEPSEEK_API_KEY='your_actual_api_key_here'
|
| 67 |
+
|
| 68 |
+
# 方法2:在脚本运行时输入
|
| 69 |
+
# 脚本会提示输入API密钥
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
### 步骤3:运行完整分析
|
| 73 |
+
```bash
|
| 74 |
+
cd /225040511/project/Hypo_Bio_OS
|
| 75 |
+
python run_alzheimer_benchmark.py
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
## 脚本详细说明
|
| 79 |
+
|
| 80 |
+
### 1. `run_alzheimer_benchmark.py`
|
| 81 |
+
|
| 82 |
+
这是主执行脚本,负责:
|
| 83 |
+
- 初始化Hypo_Bio_OS agent
|
| 84 |
+
- 配置MCP工具
|
| 85 |
+
- 执行分析查询
|
| 86 |
+
- 保存结果
|
| 87 |
+
|
| 88 |
+
**关键配置:**
|
| 89 |
+
```python
|
| 90 |
+
agent_config = {
|
| 91 |
+
"path": str(DATA_PATH), # 数据路径
|
| 92 |
+
"llm": "deepseek-chat", # 使用的LLM
|
| 93 |
+
"source": "Custom", # API源
|
| 94 |
+
"base_url": "https://api.deepseek.com/v1", # API地址
|
| 95 |
+
"api_key": os.getenv("DEEPSEEK_API_KEY", ""), # API密钥
|
| 96 |
+
"rewrite_user_query": True, # 重写查询以优化检索
|
| 97 |
+
"dynamic_mcp_registration": True, # 动态注册MCP工具
|
| 98 |
+
"mcp_server_top_k": 20, # 使用的MCP服务器数量
|
| 99 |
+
"mcp_tool_top_k": 12, # 每个服务器的工具数量
|
| 100 |
+
}
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
### 2. 分析查询
|
| 104 |
+
|
| 105 |
+
脚本使用以下查询执行分析:
|
| 106 |
+
```
|
| 107 |
+
Perform a comparative analysis of three different Alzheimer's Disease mouse models
|
| 108 |
+
(5xFAD, 3xTG-AD, and PS3O1S) to identify shared molecular pathways by performing
|
| 109 |
+
differential expression analysis, conducting pathway enrichment analysis using
|
| 110 |
+
KEGG pathways for each model and generate a final comparison which contains common
|
| 111 |
+
pathways present across all three models, corresponding p-values for each
|
| 112 |
+
pathway-model combination.
|
| 113 |
+
|
| 114 |
+
Data files available:
|
| 115 |
+
1. GSE168137_countList.txt - 5xFAD mouse model data
|
| 116 |
+
2. GSE161904_Raw_gene_counts_cortex.txt - 3xTG-AD mouse model data
|
| 117 |
+
3. DEA_PS3O1S.csv - PS3O1S tau transgenic mouse model differential expression results
|
| 118 |
+
|
| 119 |
+
Requirements:
|
| 120 |
+
1. Load and preprocess each dataset appropriately
|
| 121 |
+
2. Perform differential expression analysis for 5xFAD and 3xTG-AD models
|
| 122 |
+
3. For PS3O1S model, use the provided differential expression results
|
| 123 |
+
4. Perform KEGG pathway enrichment analysis for each model
|
| 124 |
+
5. Identify common pathways across all three models
|
| 125 |
+
6. Generate a comprehensive comparison table with p-values for each pathway-model combination
|
| 126 |
+
7. Save all intermediate results and final outputs
|
| 127 |
+
|
| 128 |
+
Please provide a detailed summary of the analysis process and findings.
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
## 预期输出
|
| 132 |
+
|
| 133 |
+
### 1. 结果文件结构
|
| 134 |
+
每次运行会在`/225040511/project/Hypo_Bio_OS/bioagent-bench-results/`下创建时间戳目录:
|
| 135 |
+
```
|
| 136 |
+
alzheimer_analysis_20260511_143022/
|
| 137 |
+
├── run_metadata.json # 运行配置和元数据
|
| 138 |
+
├── execution_log.txt # 详细执行日志
|
| 139 |
+
├── execution_log.json # JSON格式日志
|
| 140 |
+
├── final_answer.txt # agent的最终分析报告
|
| 141 |
+
├── processed_data/ # 处理后的数据文件
|
| 142 |
+
├── analysis_results/ # 分析结果
|
| 143 |
+
│ ├── differential_expression/
|
| 144 |
+
│ ├── pathway_enrichment/
|
| 145 |
+
│ └── comparative_analysis/
|
| 146 |
+
├── visualizations/ # 图表文件
|
| 147 |
+
└── reports/ # 生成报告
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
### 2. 关键分析结果
|
| 151 |
+
1. **差异表达分析结果**:每个模型的显著差异表达基因列表
|
| 152 |
+
2. **通路富集结果**:每个模型的KEGG通路富集分析结果
|
| 153 |
+
3. **共同通路表格**:三个模型共有的通路及对应p-value
|
| 154 |
+
4. **可视化图表**:火山图、通路富集图、热图等
|
| 155 |
+
5. **综合分析报告**:详细的分析过程、方法和发现总结
|
| 156 |
+
|
| 157 |
+
## 故障排除
|
| 158 |
+
|
| 159 |
+
### 常见问题1:API密钥错误
|
| 160 |
+
**症状**:脚本提示API密钥无效或未设置
|
| 161 |
+
**解决**:
|
| 162 |
+
```bash
|
| 163 |
+
# 确认环境变量已设置
|
| 164 |
+
echo $DEEPSEEK_API_KEY
|
| 165 |
+
|
| 166 |
+
# 重新设置
|
| 167 |
+
export DEEPSEEK_API_KEY='your_actual_api_key'
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
### 常见问题2:依赖缺失
|
| 171 |
+
**症状**:导入错误或运行时错误
|
| 172 |
+
**解决**:
|
| 173 |
+
```bash
|
| 174 |
+
# 激活conda环境
|
| 175 |
+
conda activate biomni_e1
|
| 176 |
+
|
| 177 |
+
# 安装缺失包
|
| 178 |
+
pip install pandas numpy scipy statsmodels mygene gseapy
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### 常见问题3:权限问题
|
| 182 |
+
**症状**:无法读取数据文件或写入结果
|
| 183 |
+
**解决**:
|
| 184 |
+
```bash
|
| 185 |
+
# 检查文件权限
|
| 186 |
+
ls -la /225040511/project/bioagent-bench/dataset/alzheimer-mouse/data/
|
| 187 |
+
|
| 188 |
+
# 检查结果目录权限
|
| 189 |
+
ls -la /225040511/project/Hypo_Bio_OS/bioagent-bench-results/
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
### 常见问题4:内存不足
|
| 193 |
+
**症状**:分析过程中程序崩溃
|
| 194 |
+
**解决**:
|
| 195 |
+
- 减少同时处理的数据量
|
| 196 |
+
- 增加系统内存
|
| 197 |
+
- 使用更高效的数据结构
|
| 198 |
+
|
| 199 |
+
## 扩展和定制
|
| 200 |
+
|
| 201 |
+
### 1. 使用不同的LLM
|
| 202 |
+
修改`run_alzheimer_benchmark.py`中的配置:
|
| 203 |
+
```python
|
| 204 |
+
# 使用OpenAI
|
| 205 |
+
agent_config = {
|
| 206 |
+
"llm": "gpt-4",
|
| 207 |
+
"source": "OpenAI",
|
| 208 |
+
"api_key": os.getenv("OPENAI_API_KEY", ""),
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
# 使用Claude
|
| 212 |
+
agent_config = {
|
| 213 |
+
"llm": "claude-3-5-sonnet-20241022",
|
| 214 |
+
"source": "Anthropic",
|
| 215 |
+
"api_key": os.getenv("ANTHROPIC_API_KEY", ""),
|
| 216 |
+
}
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
### 2. 添加自定义分析步骤
|
| 220 |
+
在查询中添加特定要求:
|
| 221 |
+
```python
|
| 222 |
+
custom_query = QUERY + """
|
| 223 |
+
|
| 224 |
+
额外要求:
|
| 225 |
+
1. 执行蛋白质互作网络分析
|
| 226 |
+
2. 识别hub基因
|
| 227 |
+
3. 与人类Alzheimer's Disease数据进行比较
|
| 228 |
+
"""
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
### 3. 调整输出格式
|
| 232 |
+
修改`save_results`函数以生成特定格式的输出。
|
| 233 |
+
|
| 234 |
+
## 性能优化建议
|
| 235 |
+
|
| 236 |
+
1. **数据预处理**:对大型计数矩阵进行适当的下采样或过滤
|
| 237 |
+
2. **并行处理**:对独立分析步骤使用多进程
|
| 238 |
+
3. **缓存中间结果**:避免重复计算
|
| 239 |
+
4. **内存管理**:及时释放不再需要的大型对象
|
| 240 |
+
|
| 241 |
+
## 参考资源
|
| 242 |
+
|
| 243 |
+
1. **原始benchmark**:`/225040511/project/bioagent-bench/README.md`
|
| 244 |
+
2. **Hypo_Bio_OS文档**:`/225040511/project/Hypo_Bio_OS/README.md`
|
| 245 |
+
3. **相关论文**:
|
| 246 |
+
- 5xFAD模型:GSE168137
|
| 247 |
+
- 3xTG-AD模型:GSE161904
|
| 248 |
+
- PS3O1S模型:GSE118523
|
| 249 |
+
|
| 250 |
+
## 支持
|
| 251 |
+
|
| 252 |
+
如遇问题,请:
|
| 253 |
+
1. 检查日志文件中的详细错误信息
|
| 254 |
+
2. 验证数据文件和权限
|
| 255 |
+
3. 确认API密钥和网络连接
|
| 256 |
+
4. 参考Hypo_Bio_OS系统文档
|
| 257 |
+
|
| 258 |
+
---
|
| 259 |
+
|
| 260 |
+
*最后更新:2026年5月11日*
|
| 261 |
+
*作者:Hypo_Bio_OS开发团队*
|
Biomanus/BENCHMARK_EXECUTION_SUMMARY.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Alzheimer's Disease Mouse Models Benchmark 执行总结
|
| 2 |
+
|
| 3 |
+
## 项目概述
|
| 4 |
+
|
| 5 |
+
成功创建了使用Hypo_Bio_OS agent系统执行Alzheimer's Disease mouse models benchmark分析的完整解决方案。该方案能够自动执行三个小鼠模型(5xFAD、3xTG-AD、PS3O1S)的比较分析,识别共享分子通路。
|
| 6 |
+
|
| 7 |
+
## 完成的工作
|
| 8 |
+
|
| 9 |
+
### 1. 系统分析
|
| 10 |
+
- ✅ 分析了bioagent-bench benchmark的结构
|
| 11 |
+
- ✅ 研究了Alzheimer's mouse数据文件格式
|
| 12 |
+
- ✅ 理解了Hypo_Bio_OS agent系统的架构
|
| 13 |
+
- ✅ 掌握了agent调用接口和配置方法
|
| 14 |
+
|
| 15 |
+
### 2. 代码开发
|
| 16 |
+
- ✅ 创建了主执行脚本 `run_alzheimer_benchmark.py`
|
| 17 |
+
- ✅ 开发了测试脚本 `test_alzheimer_analysis.py`
|
| 18 |
+
- ✅ 实现了数据访问验证脚本 `verify_data_access.py`
|
| 19 |
+
- ✅ 编写了Shell执行脚本 `run_benchmark.sh`
|
| 20 |
+
|
| 21 |
+
### 3. 配置设置
|
| 22 |
+
- ✅ 设置了结果保存路径 `/225040511/project/Hypo_Bio_OS/bioagent-bench-results`
|
| 23 |
+
- ✅ 配置了MCP工具集成
|
| 24 |
+
- ✅ 定义了标准化的输出格式
|
| 25 |
+
- ✅ 创建了目录结构模板
|
| 26 |
+
|
| 27 |
+
### 4. 文档编写
|
| 28 |
+
- ✅ 编写了详细执行指南 `ALZHEIMER_BENCHMARK_GUIDE.md`
|
| 29 |
+
- ✅ 创建了结果目录说明 `README.md`
|
| 30 |
+
- ✅ 提供了故障排除指南
|
| 31 |
+
- ✅ 记录了性能优化建议
|
| 32 |
+
|
| 33 |
+
## 技术架构
|
| 34 |
+
|
| 35 |
+
### 核心组件
|
| 36 |
+
1. **Hypo_Bio_OS Agent**:基于LangGraph的智能体系统
|
| 37 |
+
2. **MCP工具集成**:通过strata_mcp_config_shim.yaml配置
|
| 38 |
+
3. **数据分析管道**:差异表达分析 + 通路富集分析
|
| 39 |
+
4. **结果管理系统**:结构化输出和元数据记录
|
| 40 |
+
|
| 41 |
+
### 数据流程
|
| 42 |
+
```
|
| 43 |
+
原始数据文件 → 数据预处理 → 差异表达分析 →
|
| 44 |
+
通路富集分析 → 比较分析 → 结果生成 → 报告输出
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
### 支持的数据集
|
| 48 |
+
1. **5xFAD模型**:GSE168137_countList.txt
|
| 49 |
+
2. **3xTG-AD模型**:GSE161904_Raw_gene_counts_cortex.txt
|
| 50 |
+
3. **PS3O1S模型**:DEA_PS3O1S.csv(已包含差异表达结果)
|
| 51 |
+
|
| 52 |
+
## 功能特性
|
| 53 |
+
|
| 54 |
+
### 1. 自动化分析
|
| 55 |
+
- 自动加载和预处理三个数据集
|
| 56 |
+
- 执行差异表达分析(5xFAD和3xTG-AD)
|
| 57 |
+
- 利用现有差异表达结果(PS3O1S)
|
| 58 |
+
- 进行KEGG通路富集分析
|
| 59 |
+
- 识别共有通路并生成比较表格
|
| 60 |
+
|
| 61 |
+
### 2. 智能配置
|
| 62 |
+
- 支持多种LLM(DeepSeek、OpenAI、Claude等)
|
| 63 |
+
- 动态MCP工具注册和选择
|
| 64 |
+
- 可配置的分析参数
|
| 65 |
+
- 灵活的输出格式
|
| 66 |
+
|
| 67 |
+
### 3. 结果管理
|
| 68 |
+
- 时间戳目录自动创建
|
| 69 |
+
- 完整的元数据记录
|
| 70 |
+
- 结构化文件存储
|
| 71 |
+
- 可视化图表生成
|
| 72 |
+
|
| 73 |
+
### 4. 错误处理
|
| 74 |
+
- 数据文件验证
|
| 75 |
+
- API密钥检查
|
| 76 |
+
- 依赖缺失检测
|
| 77 |
+
- 详细错误日志
|
| 78 |
+
|
| 79 |
+
## 使用方法
|
| 80 |
+
|
| 81 |
+
### 快速开始
|
| 82 |
+
```bash
|
| 83 |
+
cd /225040511/project/Hypo_Bio_OS
|
| 84 |
+
|
| 85 |
+
# 设置API密钥
|
| 86 |
+
export DEEPSEEK_API_KEY='your_api_key'
|
| 87 |
+
|
| 88 |
+
# 运行分析
|
| 89 |
+
./run_benchmark.sh
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
### 分步执行
|
| 93 |
+
```bash
|
| 94 |
+
# 1. 验证数据访问
|
| 95 |
+
python verify_data_access.py
|
| 96 |
+
|
| 97 |
+
# 2. 运行完整分析
|
| 98 |
+
python run_alzheimer_benchmark.py
|
| 99 |
+
|
| 100 |
+
# 3. 查看结果
|
| 101 |
+
ls -la /225040511/project/Hypo_Bio_OS/bioagent-bench-results/
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
## 预期输出
|
| 105 |
+
|
| 106 |
+
### 分析结果
|
| 107 |
+
1. **差异表达基因列表**:每个模型的显著差异表达基因
|
| 108 |
+
2. **通路富集结果**:KEGG通路富集分析结果
|
| 109 |
+
3. **共同通路表格**:三个模型共有的通路及p-value
|
| 110 |
+
4. **可视化图表**:火山图、富集图、热图等
|
| 111 |
+
|
| 112 |
+
### 报告文件
|
| 113 |
+
1. **技术报告**:详细的分析方法和过程
|
| 114 |
+
2. **结果摘要**:关键发现和结论
|
| 115 |
+
3. **元数据文件**:运行配置和环境信息
|
| 116 |
+
4. **执行日志**:完整的运行记录
|
| 117 |
+
|
| 118 |
+
## 扩展能力
|
| 119 |
+
|
| 120 |
+
### 自定义分析
|
| 121 |
+
- 添加新的分析步骤
|
| 122 |
+
- 修改分析参数
|
| 123 |
+
- 集成额外数据源
|
| 124 |
+
- 生成定制化报告
|
| 125 |
+
|
| 126 |
+
### 性能优化
|
| 127 |
+
- 并行处理支持
|
| 128 |
+
- 内存管理优化
|
| 129 |
+
- 缓存机制
|
| 130 |
+
- 增量分析
|
| 131 |
+
|
| 132 |
+
### 集成扩展
|
| 133 |
+
- 与其他生物信息学工具集成
|
| 134 |
+
- 支持更多数据格式
|
| 135 |
+
- 云平台部署
|
| 136 |
+
- 工作流自动化
|
| 137 |
+
|
| 138 |
+
## 验证状态
|
| 139 |
+
|
| 140 |
+
### 已验证的功能
|
| 141 |
+
- ✅ 数据文件访问权限
|
| 142 |
+
- ✅ Python环境兼容性
|
| 143 |
+
- ✅ 脚本基本功能
|
| 144 |
+
- ✅ 目录结构创建
|
| 145 |
+
|
| 146 |
+
### 待验证的功能
|
| 147 |
+
- ⏳ 完整agent执行(需要API密钥)
|
| 148 |
+
- ⏳ 实际分析结果质量
|
| 149 |
+
- ⏳ 大规模数据处理性能
|
| 150 |
+
- ⏳ 错误恢复机制
|
| 151 |
+
|
| 152 |
+
## 后续建议
|
| 153 |
+
|
| 154 |
+
### 短期改进
|
| 155 |
+
1. 添加更多测试用例
|
| 156 |
+
2. 优化内存使用
|
| 157 |
+
3. 改进错误消息
|
| 158 |
+
4. 增加进度指示
|
| 159 |
+
|
| 160 |
+
### 长期规划
|
| 161 |
+
1. 支持更多小鼠模型
|
| 162 |
+
2. 集成单细胞分析
|
| 163 |
+
3. 添加机器学习组件
|
| 164 |
+
4. 开发Web界面
|
| 165 |
+
|
| 166 |
+
## 贡献指南
|
| 167 |
+
|
| 168 |
+
欢迎通过以下方式贡献:
|
| 169 |
+
1. 报告问题和建议
|
| 170 |
+
2. 提交代码改进
|
| 171 |
+
3. 添加新的分析功能
|
| 172 |
+
4. 完善文档和示例
|
| 173 |
+
|
| 174 |
+
## 联系信息
|
| 175 |
+
|
| 176 |
+
- 项目位置:`/225040511/project/Hypo_Bio_OS`
|
| 177 |
+
- 数据源:`/225040511/project/bioagent-bench`
|
| 178 |
+
- 结果目录:`/225040511/project/Hypo_Bio_OS/bioagent-bench-results`
|
| 179 |
+
|
| 180 |
+
---
|
| 181 |
+
|
| 182 |
+
*执行总结生成时间:2026年5月11日*
|
| 183 |
+
*系统状态:就绪,等待API密钥执行完整分析*
|
Biomanus/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
Biomanus/LMX1B.meme
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MEME version 4
|
| 2 |
+
|
| 3 |
+
ALPHABET= ACGT
|
| 4 |
+
|
| 5 |
+
strands: + -
|
| 6 |
+
|
| 7 |
+
Background letter frequencies
|
| 8 |
+
A 0.25 C 0.25 G 0.25 T 0.25
|
| 9 |
+
|
| 10 |
+
MOTIF LMX1B
|
| 11 |
+
letter-probability matrix: alength= 4 w= 8 nsites= 4101 E= 0
|
| 12 |
+
0.211220 0.235366 0.239512 0.313902
|
| 13 |
+
0.111735 0.294216 0.034956 0.559093
|
| 14 |
+
0.791088 0.059028 0.063465 0.086420
|
| 15 |
+
0.935021 0.013680 0.009804 0.041496
|
| 16 |
+
0.070230 0.002460 0.010065 0.917244
|
| 17 |
+
0.214912 0.065318 0.066433 0.653338
|
| 18 |
+
0.871811 0.019983 0.004252 0.103954
|
| 19 |
+
0.513171 0.161463 0.197317 0.128049
|
Biomanus/MANIFEST.in
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MANIFEST.in
|
| 2 |
+
|
| 3 |
+
# Include all python files from the biomni package
|
| 4 |
+
recursive-include biomni *.py
|
| 5 |
+
|
| 6 |
+
# Include the .pkl database files
|
| 7 |
+
recursive-include biomni/tool/schema_db *.pkl
|
| 8 |
+
|
| 9 |
+
# Include specific files from biomni_env, but not the biomni_tools subdirectory
|
| 10 |
+
recursive-include biomni_env *.py *.sh *.yml *.yaml *.txt *.md *.json *.R
|
| 11 |
+
|
| 12 |
+
# Exclude specific directories that are not part of the package
|
| 13 |
+
prune tutorials/data
|
| 14 |
+
prune biomni_env/biomni_tools
|
| 15 |
+
|
| 16 |
+
# Exclude build artifacts and caches
|
| 17 |
+
global-exclude *.py[co]
|
| 18 |
+
global-exclude __pycache__
|
| 19 |
+
global-exclude .ruff_cache
|
| 20 |
+
prune build
|
| 21 |
+
prune dist
|
| 22 |
+
prune *.egg-info
|
| 23 |
+
|
| 24 |
+
# Include other necessary files
|
| 25 |
+
include README.md
|
| 26 |
+
include LICENSE
|
| 27 |
+
include pyproject.toml
|
Biomanus/README.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# HypoBioOS
|
| 2 |
+
|
| 3 |
+
Step-by-step guide for setting up Biomni and running the generic paper reproduction workflow in this repository.
|
| 4 |
+
|
| 5 |
+
This repository is developed based on the open-source Biomni codebase from [snap-stanford/biomni](https://github.com/snap-stanford/biomni). We extend the original Biomni agent framework with MCP server generation, graph-guided tool retrieval, dynamic MCP routing, and benchmark-oriented experiment runners.
|
| 6 |
+
|
| 7 |
+
## 1. Goal
|
| 8 |
+
|
| 9 |
+
This document explains how to:
|
| 10 |
+
|
| 11 |
+
- set up the Biomni runtime environment,
|
| 12 |
+
- configure the LLM endpoint used by the reproduction script,
|
| 13 |
+
- verify MCP servers are discoverable,
|
| 14 |
+
- run the generic reproduction runner,
|
| 15 |
+
- reproduce the built-in FOXJ1 example,
|
| 16 |
+
- run the same workflow on a different paper.
|
| 17 |
+
|
| 18 |
+
The main script covered here is:
|
| 19 |
+
|
| 20 |
+
```text
|
| 21 |
+
/225040511/project/Biomni/reproduce_foxj1_paper.py
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
Although the filename still contains `foxj1`, the script is now a generic runner.
|
| 25 |
+
|
| 26 |
+
## 2. Prerequisites
|
| 27 |
+
|
| 28 |
+
Before you start, make sure you have:
|
| 29 |
+
|
| 30 |
+
- access to this Biomni repository,
|
| 31 |
+
- Conda installed,
|
| 32 |
+
- a working Python environment compatible with Biomni,
|
| 33 |
+
- an API key for a model endpoint,
|
| 34 |
+
- generated MCP server directories under `biomni_web/backend/data/mcp_generated/`.
|
| 35 |
+
|
| 36 |
+
## 3. Enter The Repository
|
| 37 |
+
|
| 38 |
+
```bash
|
| 39 |
+
cd /225040511/project/Biomni
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
## 4. Build The Biomni Environment
|
| 43 |
+
|
| 44 |
+
Follow the environment setup instructions in:
|
| 45 |
+
|
| 46 |
+
```text
|
| 47 |
+
biomni_env/README.md
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
After the environment is created, activate it:
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
conda activate biomni_e1
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
If you are running Biomni from source, install the current repository into the environment:
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
pip install -e .
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
If you prefer the upstream GitHub version instead:
|
| 63 |
+
|
| 64 |
+
```bash
|
| 65 |
+
pip install git+https://github.com/snap-stanford/Biomni.git@main
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## 5. Configure The Model Endpoint
|
| 69 |
+
|
| 70 |
+
The current `reproduce_foxj1_paper.py` script creates the agent with:
|
| 71 |
+
|
| 72 |
+
```python
|
| 73 |
+
source="Custom"
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
This means the script is using an OpenAI-compatible HTTP endpoint through Biomni's custom backend path.
|
| 77 |
+
|
| 78 |
+
The current code reads credentials in this order:
|
| 79 |
+
|
| 80 |
+
1. `DEEPSEEK_API_KEY`
|
| 81 |
+
2. `OPENAI_API_KEY`
|
| 82 |
+
3. fallback to `"EMPTY"`
|
| 83 |
+
|
| 84 |
+
The current code also reads:
|
| 85 |
+
|
| 86 |
+
- `DEEPSEEK_BASE_URL`
|
| 87 |
+
- `DEEPSEEK_MODEL_NAME`
|
| 88 |
+
|
| 89 |
+
So if you want to use OpenAI, you should not only set `OPENAI_API_KEY`, but also point `DEEPSEEK_BASE_URL` to the OpenAI API endpoint and set `DEEPSEEK_MODEL_NAME` to an OpenAI model name.
|
| 90 |
+
|
| 91 |
+
### Option A. Use DeepSeek
|
| 92 |
+
|
| 93 |
+
Set these environment variables:
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
export DEEPSEEK_API_KEY="your_api_key"
|
| 97 |
+
export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
|
| 98 |
+
export DEEPSEEK_MODEL_NAME="deepseek-chat"
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### Option B. Use OpenAI API Key
|
| 102 |
+
|
| 103 |
+
Use the OpenAI key as the authentication source, but still set the endpoint and model through the same runtime variables used by the script:
|
| 104 |
+
|
| 105 |
+
```bash
|
| 106 |
+
export OPENAI_API_KEY="your_openai_api_key"
|
| 107 |
+
export DEEPSEEK_BASE_URL="https://api.openai.com/v1"
|
| 108 |
+
export DEEPSEEK_MODEL_NAME="gpt-4.1-mini"
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
You can replace `gpt-4.1-mini` with another OpenAI model that your account can access, for example:
|
| 112 |
+
|
| 113 |
+
```bash
|
| 114 |
+
export DEEPSEEK_MODEL_NAME="gpt-4.1"
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
### Option C. Use Another OpenAI-Compatible Endpoint
|
| 118 |
+
|
| 119 |
+
If you use another OpenAI-compatible endpoint, point the same variables to your own service:
|
| 120 |
+
|
| 121 |
+
```bash
|
| 122 |
+
export OPENAI_API_KEY="your_custom_endpoint_key"
|
| 123 |
+
export DEEPSEEK_BASE_URL="http://your-endpoint/v1"
|
| 124 |
+
export DEEPSEEK_MODEL_NAME="your-model-name"
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
### Quick Check
|
| 128 |
+
|
| 129 |
+
After exporting the variables, you can confirm they are set:
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
echo "$DEEPSEEK_BASE_URL"
|
| 133 |
+
echo "$DEEPSEEK_MODEL_NAME"
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
If you are using OpenAI:
|
| 137 |
+
|
| 138 |
+
```bash
|
| 139 |
+
echo "$OPENAI_API_KEY"
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
You do not need to print the full key in shared logs. It is enough to verify that the variable is non-empty.
|
| 143 |
+
|
| 144 |
+
Optional but recommended:
|
| 145 |
+
|
| 146 |
+
```bash
|
| 147 |
+
export BIOMNI_MCP_PYTHON="$(which python)"
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
This ensures the generated MCP config uses the Python interpreter from your active Biomni environment.
|
| 151 |
+
|
| 152 |
+
## 6. Verify MCP Servers Exist
|
| 153 |
+
|
| 154 |
+
The script discovers MCP servers from:
|
| 155 |
+
|
| 156 |
+
```text
|
| 157 |
+
biomni_web/backend/data/mcp_generated/
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
Each server is expected to look like:
|
| 161 |
+
|
| 162 |
+
```text
|
| 163 |
+
mcp_<server_name>/app/*_shim_server.py
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
or:
|
| 167 |
+
|
| 168 |
+
```text
|
| 169 |
+
mcp_<server_name>/app/*_server.py
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
You can inspect what the script currently finds:
|
| 173 |
+
|
| 174 |
+
```bash
|
| 175 |
+
python reproduce_foxj1_paper.py --list-servers
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
If the list is empty or missing expected servers, check whether the corresponding generated directories exist under:
|
| 179 |
+
|
| 180 |
+
```text
|
| 181 |
+
/225040511/project/Biomni/biomni_web/backend/data/mcp_generated/
|
| 182 |
+
```
|
| 183 |
+
|
| 184 |
+
## 7. Understand What The Script Does
|
| 185 |
+
|
| 186 |
+
The script performs these high-level steps:
|
| 187 |
+
|
| 188 |
+
1. parses your paper title, context, query, input files, and requested MCP servers,
|
| 189 |
+
2. discovers available MCP server directories,
|
| 190 |
+
3. builds an MCP config YAML file for the selected servers,
|
| 191 |
+
4. creates a run directory under `paper_reproduction_runs/`,
|
| 192 |
+
5. writes prompt, query, and plan files,
|
| 193 |
+
6. initializes `A1`,
|
| 194 |
+
7. loads the MCP config,
|
| 195 |
+
8. asks Biomni to plan the reproduction steps from your query,
|
| 196 |
+
9. lets Biomni select suitable MCP tools during execution,
|
| 197 |
+
10. saves logs, final answer, and report files.
|
| 198 |
+
|
| 199 |
+
## 8. Do A Dry Run First
|
| 200 |
+
|
| 201 |
+
Before running the full workflow, do a preparation-only run:
|
| 202 |
+
|
| 203 |
+
```bash
|
| 204 |
+
python reproduce_foxj1_paper.py \
|
| 205 |
+
--dataset-profile foxj1 \
|
| 206 |
+
--prepare-only
|
| 207 |
+
```
|
| 208 |
+
|
| 209 |
+
This does not call the model. It only prepares:
|
| 210 |
+
|
| 211 |
+
- the run directory,
|
| 212 |
+
- the MCP config,
|
| 213 |
+
- the query file,
|
| 214 |
+
- the prompt file,
|
| 215 |
+
- the execution plan file.
|
| 216 |
+
|
| 217 |
+
The output will be written under:
|
| 218 |
+
|
| 219 |
+
```text
|
| 220 |
+
paper_reproduction_runs/
|
| 221 |
+
```
|
| 222 |
+
|
| 223 |
+
## 9. Run The Built-In FOXJ1 Example
|
| 224 |
+
|
| 225 |
+
Use the built-in dataset profile if you want to reproduce the FOXJ1 example workflow.
|
| 226 |
+
|
| 227 |
+
Command:
|
| 228 |
+
|
| 229 |
+
```bash
|
| 230 |
+
python reproduce_foxj1_paper.py \
|
| 231 |
+
--dataset-profile foxj1 \
|
| 232 |
+
--query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
What this does:
|
| 236 |
+
|
| 237 |
+
1. loads the built-in FOXJ1 profile,
|
| 238 |
+
2. downloads the associated GEO processed files if needed,
|
| 239 |
+
3. selects the preferred MCP server set for that profile,
|
| 240 |
+
4. builds the execution prompt from your natural-language query,
|
| 241 |
+
5. runs Biomni with MCP enabled,
|
| 242 |
+
6. writes logs and outputs into the run directory.
|
| 243 |
+
|
| 244 |
+
## 10. Run A Different Paper
|
| 245 |
+
|
| 246 |
+
If you want to run another paper, provide your own paper metadata and inputs.
|
| 247 |
+
|
| 248 |
+
Minimal example:
|
| 249 |
+
|
| 250 |
+
```bash
|
| 251 |
+
python reproduce_foxj1_paper.py \
|
| 252 |
+
--paper-title "Your Paper Title" \
|
| 253 |
+
--context-file /path/to/paper_summary.md \
|
| 254 |
+
--input-file /path/to/data1.csv \
|
| 255 |
+
--input-file /path/to/data2.tsv \
|
| 256 |
+
--query "Reproduce the main findings, choose suitable MCP tools, run the feasible analyses, and summarize what is supported by the provided data." \
|
| 257 |
+
--server jq \
|
| 258 |
+
--server bioconductor-clusterprofiler \
|
| 259 |
+
--server gseapy
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
You can also pass entire directories:
|
| 263 |
+
|
| 264 |
+
```bash
|
| 265 |
+
python reproduce_foxj1_paper.py \
|
| 266 |
+
--paper-title "Another Paper" \
|
| 267 |
+
--paper-context "Short summary of the paper and the results you want to reproduce." \
|
| 268 |
+
--input-dir /path/to/input_folder \
|
| 269 |
+
--query "Check differential patterns and enrichment results." \
|
| 270 |
+
--server all
|
| 271 |
+
```
|
| 272 |
+
|
| 273 |
+
## 11. Important Command Options
|
| 274 |
+
|
| 275 |
+
Common options supported by the script:
|
| 276 |
+
|
| 277 |
+
- `--paper-title`: title of the paper for this run.
|
| 278 |
+
- `--paper-context`: short context text passed directly on the command line.
|
| 279 |
+
- `--context-file`: markdown or text file containing paper context.
|
| 280 |
+
- `--query`: natural-language reproduction request.
|
| 281 |
+
- `--input-file`: input file to include, can be used multiple times.
|
| 282 |
+
- `--input-dir`: input directory to include, can be used multiple times.
|
| 283 |
+
- `--dataset-profile foxj1`: use the built-in FOXJ1 example profile.
|
| 284 |
+
- `--server`: choose MCP servers explicitly.
|
| 285 |
+
- `--server all`: register all discovered MCP servers.
|
| 286 |
+
- `--prepare-only`: generate files without running the model.
|
| 287 |
+
- `--list-servers`: print discovered MCP servers and exit.
|
| 288 |
+
|
| 289 |
+
## 12. What Files You Should Expect
|
| 290 |
+
|
| 291 |
+
For each run, the script creates a folder like:
|
| 292 |
+
|
| 293 |
+
```text
|
| 294 |
+
paper_reproduction_runs/<run_name>/
|
| 295 |
+
```
|
| 296 |
+
|
| 297 |
+
Inside it, you should see files such as:
|
| 298 |
+
|
| 299 |
+
```text
|
| 300 |
+
mcp_config.yaml
|
| 301 |
+
paper_context.md
|
| 302 |
+
reproduction_query.txt
|
| 303 |
+
execution_plan.md
|
| 304 |
+
reproduction_prompt.txt
|
| 305 |
+
results/biomni_execution_log_*.txt
|
| 306 |
+
results/biomni_execution_log_*.json
|
| 307 |
+
results/biomni_final_answer_*.txt
|
| 308 |
+
results/biomni_run_metadata_*.json
|
| 309 |
+
results/biomni_conversation_*.md
|
| 310 |
+
results/reproduction_report.md
|
| 311 |
+
```
|
| 312 |
+
|
| 313 |
+
## 13. Recommended First-Time Workflow
|
| 314 |
+
|
| 315 |
+
If this is your first time running the script, use this exact order:
|
| 316 |
+
|
| 317 |
+
1. `cd /225040511/project/Biomni`
|
| 318 |
+
2. `conda activate biomni_e1`
|
| 319 |
+
3. `pip install -e .`
|
| 320 |
+
4. export `DEEPSEEK_API_KEY`
|
| 321 |
+
5. export `DEEPSEEK_BASE_URL`
|
| 322 |
+
6. export `DEEPSEEK_MODEL_NAME`
|
| 323 |
+
7. optionally export `BIOMNI_MCP_PYTHON="$(which python)"`
|
| 324 |
+
8. run `python reproduce_foxj1_paper.py --list-servers`
|
| 325 |
+
9. run `python reproduce_foxj1_paper.py --dataset-profile foxj1 --prepare-only`
|
| 326 |
+
10. run the full FOXJ1 example command
|
| 327 |
+
|
| 328 |
+
## 14. Troubleshooting
|
| 329 |
+
|
| 330 |
+
If the script does not run, check these items one by one.
|
| 331 |
+
|
| 332 |
+
### Environment Problems
|
| 333 |
+
|
| 334 |
+
- Make sure the Conda environment is activated.
|
| 335 |
+
- Make sure `pip install -e .` completed successfully.
|
| 336 |
+
- Make sure the same Python is used by both Biomni and MCP config generation.
|
| 337 |
+
|
| 338 |
+
### Model Problems
|
| 339 |
+
|
| 340 |
+
- Make sure `DEEPSEEK_API_KEY` is set.
|
| 341 |
+
- Make sure `DEEPSEEK_BASE_URL` points to a reachable OpenAI-compatible endpoint.
|
| 342 |
+
- Make sure `DEEPSEEK_MODEL_NAME` matches a model served by that endpoint.
|
| 343 |
+
|
| 344 |
+
### MCP Problems
|
| 345 |
+
|
| 346 |
+
- Run `python reproduce_foxj1_paper.py --list-servers`.
|
| 347 |
+
- Confirm the required `mcp_<name>` directories exist.
|
| 348 |
+
- Confirm each selected MCP server has an `app/` directory with a `*_shim_server.py` or `*_server.py` file.
|
| 349 |
+
|
| 350 |
+
### Input Problems
|
| 351 |
+
|
| 352 |
+
- Confirm all `--input-file` paths exist.
|
| 353 |
+
- Confirm all `--input-dir` paths exist.
|
| 354 |
+
- If using the `foxj1` profile, let the script finish downloading the GEO processed files.
|
| 355 |
+
|
| 356 |
+
### Dry Run Problems
|
| 357 |
+
|
| 358 |
+
- If a full run fails, first retry with `--prepare-only`.
|
| 359 |
+
- Check whether the prompt and MCP config are generated correctly before debugging model execution.
|
| 360 |
+
|
| 361 |
+
## 15. One-Line Reference Commands
|
| 362 |
+
|
| 363 |
+
List MCP servers:
|
| 364 |
+
|
| 365 |
+
```bash
|
| 366 |
+
python reproduce_foxj1_paper.py --list-servers
|
| 367 |
+
```
|
| 368 |
+
|
| 369 |
+
Prepare only:
|
| 370 |
+
|
| 371 |
+
```bash
|
| 372 |
+
python reproduce_foxj1_paper.py --dataset-profile foxj1 --prepare-only
|
| 373 |
+
```
|
| 374 |
+
|
| 375 |
+
Run FOXJ1:
|
| 376 |
+
|
| 377 |
+
```bash
|
| 378 |
+
python reproduce_foxj1_paper.py --dataset-profile foxj1 --query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
|
| 379 |
+
```
|
| 380 |
+
|
| 381 |
+
## 16. Acknowledgements
|
| 382 |
+
|
| 383 |
+
This work builds on the open-source Biomni project by the Stanford SNAP group: [snap-stanford/biomni](https://github.com/snap-stanford/biomni). We gratefully acknowledge the Biomni authors and contributors for releasing their codebase and biomedical agent framework, which provided the foundation for this development.
|
Biomanus/article.xml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
<full-text-retrieval-response xmlns="http://www.elsevier.com/xml/svapi/article/dtd" xmlns:bk="http://www.elsevier.com/xml/bk/dtd" xmlns:cals="http://www.elsevier.com/xml/common/cals/dtd" xmlns:ce="http://www.elsevier.com/xml/common/dtd" xmlns:ja="http://www.elsevier.com/xml/ja/dtd" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:sa="http://www.elsevier.com/xml/common/struct-aff/dtd" xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd" xmlns:tb="http://www.elsevier.com/xml/common/table/dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xocs="http://www.elsevier.com/xml/xocs/dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:prism="http://prismstandard.org/namespaces/basic/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><coredata><prism:url>https://api.elsevier.com/content/article/pii/S0092867419308931</prism:url><dc:identifier>doi:10.1016/j.cell.2019.08.005</dc:identifier><eid>1-s2.0-S0092867419308931</eid><prism:doi>10.1016/j.cell.2019.08.005</prism:doi><pii>S0092-8674(19)30893-1</pii><dc:title>A Structure-Informed Atlas of Human-Virus Interactions </dc:title><prism:publicationName>Cell</prism:publicationName><prism:aggregationType>Journal</prism:aggregationType><prism:issn>00928674</prism:issn><prism:coverDate>2019-09-05</prism:coverDate><prism:coverDisplayDate>5 September 2019</prism:coverDisplayDate><openaccess>1</openaccess><openaccessArticle>true</openaccessArticle><openaccessType>Full</openaccessType><openArchiveArticle>true</openArchiveArticle><openaccessSponsorName/><openaccessSponsorType>ElsevierBranded</openaccessSponsorType><openaccessUserLicense>http://www.elsevier.com/open-access/userlicense/1.0/</openaccessUserLicense><link href="https://api.elsevier.com/content/article/pii/S0092867419308931" rel="self"/><link href="https://www.sciencedirect.com/science/article/pii/S0092867419308931" rel="scidir"/></coredata></full-text-retrieval-response>
|
Biomanus/biomni.md
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<p align="center">
|
| 2 |
+
<img src="./figs/biomni_logo.png" alt="Biomni Logo" width="600px" />
|
| 3 |
+
</p>
|
| 4 |
+
|
| 5 |
+
<p align="center">
|
| 6 |
+
<a href="https://join.slack.com/t/biomnigroup/shared_invite/zt-3avks4913-dotMBt8D_apQnJ3mG~ak6Q">
|
| 7 |
+
<img src="https://img.shields.io/badge/Join-Slack-4A154B?style=for-the-badge&logo=slack" alt="Join Slack" />
|
| 8 |
+
</a>
|
| 9 |
+
<a href="https://biomni.stanford.edu">
|
| 10 |
+
<img src="https://img.shields.io/badge/Try-Web%20UI-blue?style=for-the-badge" alt="Web UI" />
|
| 11 |
+
</a>
|
| 12 |
+
<a href="https://x.com/ProjectBiomni">
|
| 13 |
+
<img src="https://img.shields.io/badge/Follow-on%20X-black?style=for-the-badge&logo=x" alt="Follow on X" />
|
| 14 |
+
</a>
|
| 15 |
+
<a href="https://www.linkedin.com/company/project-biomni">
|
| 16 |
+
<img src="https://img.shields.io/badge/Follow-LinkedIn-0077B5?style=for-the-badge&logo=linkedin" alt="Follow on LinkedIn" />
|
| 17 |
+
</a>
|
| 18 |
+
<a href="https://www.biorxiv.org/content/10.1101/2025.05.30.656746v1">
|
| 19 |
+
<img src="https://img.shields.io/badge/Read-Paper-green?style=for-the-badge" alt="Paper" />
|
| 20 |
+
</a>
|
| 21 |
+
</p>
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Biomni: A General-Purpose Biomedical AI Agent
|
| 26 |
+
|
| 27 |
+
## Overview
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
Biomni is a general-purpose biomedical AI agent designed to autonomously execute a wide range of research tasks across diverse biomedical subfields. By integrating cutting-edge large language model (LLM) reasoning with retrieval-augmented planning and code-based execution, Biomni helps scientists dramatically enhance research productivity and generate testable hypotheses.
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
## Quick Start
|
| 34 |
+
|
| 35 |
+
### Installation
|
| 36 |
+
|
| 37 |
+
Our software environment is massive and we provide a single setup.sh script to setup.
|
| 38 |
+
Follow this [file](biomni_env/README.md) to setup the env first.
|
| 39 |
+
|
| 40 |
+
Then activate the environment E1:
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
conda activate biomni_e1
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
then install the biomni official pip package:
|
| 47 |
+
|
| 48 |
+
```bash
|
| 49 |
+
pip install biomni --upgrade
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
For the latest update, install from the github source version, or do:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
pip install git+https://github.com/snap-stanford/Biomni.git@main
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Lastly, configure your API keys using one of the following methods:
|
| 59 |
+
|
| 60 |
+
<details>
|
| 61 |
+
<summary>Click to expand</summary>
|
| 62 |
+
|
| 63 |
+
#### Option 1: Using .env file (Recommended)
|
| 64 |
+
|
| 65 |
+
Create a `.env` file in your project directory:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# Copy the example file
|
| 69 |
+
cp .env.example .env
|
| 70 |
+
|
| 71 |
+
# Edit the .env file with your actual API keys
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
Your `.env` file should look like:
|
| 75 |
+
|
| 76 |
+
```env
|
| 77 |
+
# Required: Anthropic API Key for Claude models
|
| 78 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 79 |
+
|
| 80 |
+
# Optional: OpenAI API Key (if using OpenAI models)
|
| 81 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 82 |
+
|
| 83 |
+
# Optional: Azure OpenAI API Key (if using Azure OpenAI models)
|
| 84 |
+
OPENAI_API_KEY=your_azure_openai_api_key
|
| 85 |
+
OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com/
|
| 86 |
+
|
| 87 |
+
# Optional: AI Studio Gemini API Key (if using Gemini models)
|
| 88 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 89 |
+
|
| 90 |
+
# Optional: groq API Key (if using groq as model provider)
|
| 91 |
+
GROQ_API_KEY=your_groq_api_key_here
|
| 92 |
+
|
| 93 |
+
# Optional: Set the source of your LLM for example:
|
| 94 |
+
#"OpenAI", "AzureOpenAI", "Anthropic", "Ollama", "Gemini", "Bedrock", "Groq", "Custom"
|
| 95 |
+
LLM_SOURCE=your_LLM_source_here
|
| 96 |
+
|
| 97 |
+
# Optional: AWS Bedrock Configuration (if using AWS Bedrock models)
|
| 98 |
+
AWS_BEARER_TOKEN_BEDROCK=your_bedrock_api_key_here
|
| 99 |
+
AWS_REGION=us-east-1
|
| 100 |
+
|
| 101 |
+
# Optional: Custom model serving configuration
|
| 102 |
+
# CUSTOM_MODEL_BASE_URL=http://localhost:8000/v1
|
| 103 |
+
# CUSTOM_MODEL_API_KEY=your_custom_api_key_here
|
| 104 |
+
|
| 105 |
+
# Optional: Biomni data path (defaults to ./data)
|
| 106 |
+
# BIOMNI_DATA_PATH=/path/to/your/data
|
| 107 |
+
|
| 108 |
+
# Optional: Timeout settings (defaults to 600 seconds)
|
| 109 |
+
# BIOMNI_TIMEOUT_SECONDS=600
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
#### Option 2: Using shell environment variables
|
| 113 |
+
|
| 114 |
+
Alternatively, configure your API keys in bash profile `~/.bashrc`:
|
| 115 |
+
|
| 116 |
+
```bash
|
| 117 |
+
export ANTHROPIC_API_KEY="YOUR_API_KEY"
|
| 118 |
+
export OPENAI_API_KEY="YOUR_API_KEY" # optional if you just use Claude
|
| 119 |
+
export OPENAI_ENDPOINT="https://your-resource-name.openai.azure.com/" # optional unless you are using Azure
|
| 120 |
+
export AWS_BEARER_TOKEN_BEDROCK="YOUR_BEDROCK_API_KEY" # optional for AWS Bedrock models
|
| 121 |
+
export AWS_REGION="us-east-1" # optional, defaults to us-east-1 for Bedrock
|
| 122 |
+
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY" #optional if you want to use a gemini model
|
| 123 |
+
export GROQ_API_KEY="YOUR_GROQ_API_KEY" # Optional: set this to use models served by Groq
|
| 124 |
+
export LLM_SOURCE="Groq" # Optional: set this to use models served by Groq
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
```
|
| 128 |
+
</details>
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
#### ⚠️ Known Package Conflicts
|
| 132 |
+
|
| 133 |
+
Some Python packages are not installed by default in the Biomni environment due to dependency conflicts. If you need these features, you must install the packages manually and may need to uncomment relevant code in the codebase. See the up-to-date list and details in [docs/known_conflicts.md](./docs/known_conflicts.md).
|
| 134 |
+
|
| 135 |
+
### Basic Usage
|
| 136 |
+
|
| 137 |
+
Once inside the environment, you can start using Biomni:
|
| 138 |
+
|
| 139 |
+
```python
|
| 140 |
+
from biomni.agent import A1
|
| 141 |
+
|
| 142 |
+
# Initialize the agent with data path, Data lake will be automatically downloaded on first run (~11GB)
|
| 143 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 144 |
+
|
| 145 |
+
# Execute biomedical tasks using natural language
|
| 146 |
+
agent.go("Plan a CRISPR screen to identify genes that regulate T cell exhaustion, generate 32 genes that maximize the perturbation effect.")
|
| 147 |
+
agent.go("Perform scRNA-seq annotation at [PATH] and generate meaningful hypothesis")
|
| 148 |
+
agent.go("Predict ADMET properties for this compound: CC(C)CC1=CC=C(C=C1)C(C)C(=O)O")
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
#### Controlling Datalake Loading
|
| 152 |
+
|
| 153 |
+
By default, Biomni automatically downloads the datalake files (~11GB) when you create an agent. You can control this behavior:
|
| 154 |
+
|
| 155 |
+
```python
|
| 156 |
+
# Skip automatic datalake download (faster initialization)
|
| 157 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514', expected_data_lake_files = [])
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
This is useful for:
|
| 161 |
+
- Faster testing and development
|
| 162 |
+
- Environments with limited storage or bandwidth
|
| 163 |
+
- Cases where you only need specific tools that don't require datalake files
|
| 164 |
+
If you plan on using Azure for your model, always prefix the model name with azure- (e.g. llm='azure-gpt-4o').
|
| 165 |
+
|
| 166 |
+
### Gradio Interface
|
| 167 |
+
|
| 168 |
+
Launch an interactive web UI for Biomni:
|
| 169 |
+
|
| 170 |
+
```python
|
| 171 |
+
from biomni.agent import A1
|
| 172 |
+
|
| 173 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 174 |
+
agent.launch_gradio_demo()
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
**Installation:**
|
| 178 |
+
```bash
|
| 179 |
+
pip install "gradio>=5.0,<6.0"
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
**Note:** Biomni's Gradio interface currently requires Gradio 5.x due to API changes in Gradio 6.0. If you have Gradio 6.x installed, you may need to downgrade.
|
| 183 |
+
|
| 184 |
+
**Options:**
|
| 185 |
+
- `share=True` - Create a public shareable link
|
| 186 |
+
- `server_name="127.0.0.1"` - Localhost only (default: "0.0.0.0")
|
| 187 |
+
- `require_verification=True` - Require access code (default code: "Biomni2025")
|
| 188 |
+
|
| 189 |
+
The interface will be available at `http://localhost:7860`
|
| 190 |
+
|
| 191 |
+
### Configuration Management
|
| 192 |
+
|
| 193 |
+
Biomni includes a centralized configuration system that provides flexible ways to manage settings. You can configure Biomni through environment variables, runtime modifications, or direct parameters.
|
| 194 |
+
|
| 195 |
+
```python
|
| 196 |
+
from biomni.config import default_config
|
| 197 |
+
from biomni.agent import A1
|
| 198 |
+
|
| 199 |
+
# RECOMMENDED: Modify global defaults for consistency
|
| 200 |
+
default_config.llm = "gpt-4"
|
| 201 |
+
default_config.timeout_seconds = 1200
|
| 202 |
+
|
| 203 |
+
# All agents AND database queries use these defaults
|
| 204 |
+
agent = A1() # Everything uses gpt-4, 1200s timeout
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
+
**Note**: Direct parameters to `A1()` only affect that agent's reasoning, not database queries. For consistent configuration across all operations, use `default_config` or environment variables.
|
| 208 |
+
|
| 209 |
+
For detailed configuration options, see the **[Configuration Guide](docs/configuration.md)**.
|
| 210 |
+
|
| 211 |
+
### PDF Generation
|
| 212 |
+
|
| 213 |
+
Generate PDF reports of execution traces:
|
| 214 |
+
|
| 215 |
+
```python
|
| 216 |
+
from biomni.agent import A1
|
| 217 |
+
|
| 218 |
+
# Initialize agent
|
| 219 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 220 |
+
|
| 221 |
+
# Run your task
|
| 222 |
+
agent.go("Your biomedical task here")
|
| 223 |
+
|
| 224 |
+
# Save conversation history as PDF
|
| 225 |
+
agent.save_conversation_history("my_analysis_results.pdf")
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
**PDF Generation Dependencies:**
|
| 229 |
+
<details>
|
| 230 |
+
<summary>Click to expand</summary>
|
| 231 |
+
For optimal PDF generation, install one of these packages:
|
| 232 |
+
|
| 233 |
+
```bash
|
| 234 |
+
# Option 1: WeasyPrint (recommended for best layout control)
|
| 235 |
+
# Conda environment (recommended)
|
| 236 |
+
conda install weasyprint
|
| 237 |
+
|
| 238 |
+
# System installation
|
| 239 |
+
brew install weasyprint # macOS
|
| 240 |
+
apt install weasyprint # Linux
|
| 241 |
+
|
| 242 |
+
# See [WeasyPrint Installation Guide](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html) for detailed instructions.
|
| 243 |
+
|
| 244 |
+
# Option 2: markdown2pdf (Rust-based, fast and reliable)
|
| 245 |
+
# macOS:
|
| 246 |
+
brew install theiskaa/tap/markdown2pdf
|
| 247 |
+
|
| 248 |
+
# Windows/Linux (using Cargo):
|
| 249 |
+
cargo install markdown2pdf
|
| 250 |
+
|
| 251 |
+
# Or download prebuilt binaries from:
|
| 252 |
+
# https://github.com/theiskaa/markdown2pdf/releases/latest
|
| 253 |
+
|
| 254 |
+
# Option 3: Pandoc (pip installation)
|
| 255 |
+
pip install pandoc
|
| 256 |
+
```
|
| 257 |
+
</details>
|
| 258 |
+
|
| 259 |
+
## MCP (Model Context Protocol) Support
|
| 260 |
+
|
| 261 |
+
Biomni supports MCP servers for external tool integration:
|
| 262 |
+
|
| 263 |
+
```python
|
| 264 |
+
from biomni.agent import A1
|
| 265 |
+
|
| 266 |
+
agent = A1()
|
| 267 |
+
agent.add_mcp(config_path="./mcp_config.yaml")
|
| 268 |
+
agent.go("Find FDA active ingredient information for ibuprofen")
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
**Built-in MCP Servers:**
|
| 272 |
+
For usage and implementation details, see the [MCP Integration Documentation](docs/mcp_integration.md) and examples in [`tutorials/examples/add_mcp_server/`](tutorials/examples/add_mcp_server/) and [`tutorials/examples/expose_biomni_server/`](tutorials/examples/expose_biomni_server/).
|
| 273 |
+
|
| 274 |
+
### Run The Generic Paper Reproduction Script
|
| 275 |
+
|
| 276 |
+
This repository also includes a reusable paper reproduction runner at [`reproduce_foxj1_paper.py`](./reproduce_foxj1_paper.py). Despite the filename, the script is now a generic template: you can provide a paper title, paper context, local input files, and a natural-language query, and Biomni will plan the reproduction steps and select suitable MCP tools from the registered server set.
|
| 277 |
+
|
| 278 |
+
#### 1. Base Environment
|
| 279 |
+
|
| 280 |
+
Follow the installation steps above first, then activate the Biomni environment:
|
| 281 |
+
|
| 282 |
+
```bash
|
| 283 |
+
conda activate biomni_e1
|
| 284 |
+
```
|
| 285 |
+
|
| 286 |
+
If you are working from source, install Biomni from the current repository or from GitHub:
|
| 287 |
+
|
| 288 |
+
```bash
|
| 289 |
+
pip install -e .
|
| 290 |
+
# or
|
| 291 |
+
pip install git+https://github.com/snap-stanford/Biomni.git@main
|
| 292 |
+
```
|
| 293 |
+
|
| 294 |
+
#### 2. LLM Configuration For This Script
|
| 295 |
+
|
| 296 |
+
The current implementation of `reproduce_foxj1_paper.py` builds the agent with `source="Custom"`, so the simplest working setup is to provide a DeepSeek-compatible endpoint:
|
| 297 |
+
|
| 298 |
+
```bash
|
| 299 |
+
export DEEPSEEK_API_KEY="your_api_key"
|
| 300 |
+
export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
|
| 301 |
+
export DEEPSEEK_MODEL_NAME="deepseek-chat"
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
If you use another OpenAI-compatible endpoint, point the script to that endpoint instead:
|
| 305 |
+
|
| 306 |
+
```bash
|
| 307 |
+
export DEEPSEEK_API_KEY="your_custom_endpoint_key"
|
| 308 |
+
export DEEPSEEK_BASE_URL="http://your-openai-compatible-endpoint/v1"
|
| 309 |
+
export DEEPSEEK_MODEL_NAME="your-model-name"
|
| 310 |
+
```
|
| 311 |
+
|
| 312 |
+
Optional:
|
| 313 |
+
|
| 314 |
+
```bash
|
| 315 |
+
export BIOMNI_MCP_PYTHON="$(which python)"
|
| 316 |
+
```
|
| 317 |
+
|
| 318 |
+
This helps ensure the generated MCP config uses the same Python interpreter as your active Biomni environment.
|
| 319 |
+
|
| 320 |
+
#### 3. MCP Server Availability
|
| 321 |
+
|
| 322 |
+
The script discovers MCP servers from:
|
| 323 |
+
|
| 324 |
+
```text
|
| 325 |
+
biomni_web/backend/data/mcp_generated/
|
| 326 |
+
```
|
| 327 |
+
|
| 328 |
+
For each selected server, it looks for:
|
| 329 |
+
|
| 330 |
+
- `mcp_<server_name>/app/*_shim_server.py`
|
| 331 |
+
- or `mcp_<server_name>/app/*_server.py`
|
| 332 |
+
|
| 333 |
+
For example, the following generated servers should exist if you want to run the bundled FOXJ1 example:
|
| 334 |
+
|
| 335 |
+
- `mcp_jq`
|
| 336 |
+
- `mcp_bioconductor-clusterprofiler`
|
| 337 |
+
- `mcp_bioconductor-fgsea`
|
| 338 |
+
- `mcp_bioconductor-qvalue`
|
| 339 |
+
- `mcp_gseapy`
|
| 340 |
+
|
| 341 |
+
You can inspect what the script currently discovers with:
|
| 342 |
+
|
| 343 |
+
```bash
|
| 344 |
+
python reproduce_foxj1_paper.py --list-servers
|
| 345 |
+
```
|
| 346 |
+
|
| 347 |
+
#### 4. Minimal Dry Run
|
| 348 |
+
|
| 349 |
+
To only generate the run directory, prompt, plan, and MCP config without calling the model:
|
| 350 |
+
|
| 351 |
+
```bash
|
| 352 |
+
python reproduce_foxj1_paper.py \
|
| 353 |
+
--dataset-profile foxj1 \
|
| 354 |
+
--prepare-only
|
| 355 |
+
```
|
| 356 |
+
|
| 357 |
+
This creates a run folder under:
|
| 358 |
+
|
| 359 |
+
```text
|
| 360 |
+
paper_reproduction_runs/
|
| 361 |
+
```
|
| 362 |
+
|
| 363 |
+
#### 5. Run The FOXJ1 Example
|
| 364 |
+
|
| 365 |
+
The built-in `foxj1` profile downloads the GEO processed files used by the original example and registers the preferred MCP server set automatically:
|
| 366 |
+
|
| 367 |
+
```bash
|
| 368 |
+
python reproduce_foxj1_paper.py \
|
| 369 |
+
--dataset-profile foxj1 \
|
| 370 |
+
--query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
|
| 371 |
+
```
|
| 372 |
+
|
| 373 |
+
#### 6. Run A Different Paper
|
| 374 |
+
|
| 375 |
+
For a different paper, provide your own metadata and inputs:
|
| 376 |
+
|
| 377 |
+
```bash
|
| 378 |
+
python reproduce_foxj1_paper.py \
|
| 379 |
+
--paper-title "Your Paper Title" \
|
| 380 |
+
--context-file /path/to/paper_summary.md \
|
| 381 |
+
--input-file /path/to/data1.csv \
|
| 382 |
+
--input-file /path/to/data2.tsv \
|
| 383 |
+
--query "Reproduce the main findings, run the feasible analyses, and summarize what is supported by the provided data." \
|
| 384 |
+
--server jq \
|
| 385 |
+
--server bioconductor-clusterprofiler \
|
| 386 |
+
--server gseapy
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
#### 7. What The Script Produces
|
| 390 |
+
|
| 391 |
+
Each run writes artifacts under `paper_reproduction_runs/<run_name>/`, including:
|
| 392 |
+
|
| 393 |
+
- `mcp_config.yaml`
|
| 394 |
+
- `paper_context.md`
|
| 395 |
+
- `reproduction_query.txt`
|
| 396 |
+
- `execution_plan.md`
|
| 397 |
+
- `reproduction_prompt.txt`
|
| 398 |
+
- `results/biomni_execution_log_*.txt`
|
| 399 |
+
- `results/biomni_final_answer_*.txt`
|
| 400 |
+
- `results/reproduction_report.md`
|
| 401 |
+
|
| 402 |
+
#### 8. Common Checks
|
| 403 |
+
|
| 404 |
+
If the run fails early, check the following first:
|
| 405 |
+
|
| 406 |
+
- Your Biomni environment is activated.
|
| 407 |
+
- `DEEPSEEK_API_KEY` and related endpoint variables are set.
|
| 408 |
+
- The requested MCP servers actually exist under `biomni_web/backend/data/mcp_generated/`.
|
| 409 |
+
- `python reproduce_foxj1_paper.py --list-servers` returns the servers you expect.
|
| 410 |
+
- `--prepare-only` works before attempting a full run.
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
## Biomni-R0
|
| 414 |
+
|
| 415 |
+
**Biomni-R0** is our first reasoning model for biology, built on Qwen-32B with reinforcement learning from agent interaction data. It's designed to excel at tool use, multi-step reasoning, and complex biological problem-solving through iterative self-correction.
|
| 416 |
+
|
| 417 |
+
- 🤗 Model: [biomni/Biomni-R0-32B-Preview](https://huggingface.co/biomni/Biomni-R0-32B-Preview)
|
| 418 |
+
- 📝 Technical Report: [biomni.stanford.edu/blog/biomni-r0-technical-report](https://biomni.stanford.edu/blog/biomni-r0-technical-report)
|
| 419 |
+
|
| 420 |
+
To use Biomni-R0 for agent reasoning while keeping database queries on your usual provider (recommended), run a local SGLang server and pass the model to `A1()` directly.
|
| 421 |
+
|
| 422 |
+
1) Launch SGLang with Biomni-R0:
|
| 423 |
+
|
| 424 |
+
```bash
|
| 425 |
+
python -m sglang.launch_server --model-path RyanLi0802/Biomni-R0-Preview --port 30000 --host 0.0.0.0 --mem-fraction-static 0.8 --tp 2 --trust-remote-code --json-model-override-args '{"rope_scaling":{"rope_type":"yarn","factor":1.0,"original_max_position_embeddings":32768}, "max_position_embeddings": 131072}'
|
| 426 |
+
```
|
| 427 |
+
|
| 428 |
+
2) Point the agent to your SGLang endpoint for reasoning:
|
| 429 |
+
|
| 430 |
+
```python
|
| 431 |
+
from biomni.config import default_config
|
| 432 |
+
from biomni.agent import A1
|
| 433 |
+
|
| 434 |
+
# Database queries (indexes, retrieval, etc.) use default_config
|
| 435 |
+
default_config.llm = "claude-3-5-sonnet-20241022"
|
| 436 |
+
default_config.source = "Anthropic"
|
| 437 |
+
|
| 438 |
+
# Agent reasoning uses Biomni-R0 served via SGLang (OpenAI-compatible API)
|
| 439 |
+
agent = A1(
|
| 440 |
+
llm="biomni/Biomni-R0-32B-Preview",
|
| 441 |
+
source="Custom",
|
| 442 |
+
base_url="http://localhost:30000/v1",
|
| 443 |
+
api_key="EMPTY",
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
agent.go("Plan a CRISPR screen to identify genes regulating T cell exhaustion")
|
| 447 |
+
```
|
| 448 |
+
|
| 449 |
+
## Biomni-Eval1
|
| 450 |
+
|
| 451 |
+
**Biomni-Eval1** is a comprehensive evaluation benchmark for assessing biological reasoning capabilities across diverse tasks. It contains **433 instances** spanning **10 biological reasoning tasks**, from gene identification to disease diagnosis.
|
| 452 |
+
|
| 453 |
+
**Tasks Included:**
|
| 454 |
+
- GWAS causal gene identification (3 variants)
|
| 455 |
+
- Lab bench Q&A (2 variants)
|
| 456 |
+
- Patient gene detection
|
| 457 |
+
- Screen gene retrieval
|
| 458 |
+
- GWAS variant prioritization
|
| 459 |
+
- Rare disease diagnosis
|
| 460 |
+
- CRISPR delivery method selection
|
| 461 |
+
|
| 462 |
+
**Resources:**
|
| 463 |
+
- 🤗 Dataset: [biomni/Eval1](https://huggingface.co/datasets/biomni/Eval1)
|
| 464 |
+
- 💻 Quick Start:
|
| 465 |
+
```python
|
| 466 |
+
from biomni.eval import BiomniEval1
|
| 467 |
+
|
| 468 |
+
evaluator = BiomniEval1()
|
| 469 |
+
score = evaluator.evaluate('gwas_causal_gene_opentargets', 0, 'BRCA1')
|
| 470 |
+
```
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
## 📚 Know-How Library
|
| 474 |
+
|
| 475 |
+
Biomni includes a **Know-How Library** — a curated collection of best practices, protocols, and troubleshooting guides for biomedical techniques. These documents are automatically retrieved by the A1 agent when relevant to provide domain expertise and practical knowledge.
|
| 476 |
+
|
| 477 |
+
**Features:**
|
| 478 |
+
- Automatic retrieval based on query relevance
|
| 479 |
+
- Metadata tracking (authors, affiliations, licensing, commercial use)
|
| 480 |
+
- Compatible with commercial mode (filters non-commercial content)
|
| 481 |
+
|
| 482 |
+
### 📝 Contributing Know-How Documents
|
| 483 |
+
|
| 484 |
+
We're actively seeking community contributions to expand our Know-How Library! Share your expertise by contributing:
|
| 485 |
+
|
| 486 |
+
- **Lab protocols** (cell culture, flow cytometry, western blotting, etc.)
|
| 487 |
+
- **Analysis best practices** (NGS workflows, microscopy techniques, etc.)
|
| 488 |
+
- **Troubleshooting guides** (common issues and solutions)
|
| 489 |
+
- **Experimental design guidelines** (sample size, controls, validation)
|
| 490 |
+
- **Domain-specific knowledge** (drug formulation, animal models, clinical trials, etc.)
|
| 491 |
+
|
| 492 |
+
Know-how documents should be practical, succinct, and include proper attribution. Use [this know-how](know_how/single_cell_annotation.md) as an example.
|
| 493 |
+
|
| 494 |
+
**To contribute:** Create a markdown file following our template and submit a pull request.
|
| 495 |
+
|
| 496 |
+
## 🤝 Contributing to Biomni
|
| 497 |
+
|
| 498 |
+
Biomni is an open-science initiative that thrives on community contributions. We welcome:
|
| 499 |
+
|
| 500 |
+
- **🔧 New Tools**: Specialized analysis functions and algorithms
|
| 501 |
+
- **📊 Datasets**: Curated biomedical data and knowledge bases
|
| 502 |
+
- **💻 Software**: Integration of existing biomedical software packages
|
| 503 |
+
- **📋 Benchmarks**: Evaluation datasets and performance metrics
|
| 504 |
+
- **📚 Know-How**: Best practices, protocols, and domain expertise
|
| 505 |
+
- **📚 Misc**: Tutorials, examples, and use cases
|
| 506 |
+
- **🔧 Update existing tools**: many current tools are not optimized - fix and replacements are welcome!
|
| 507 |
+
|
| 508 |
+
Check out this **[Contributing Guide](CONTRIBUTION.md)** on how to contribute to the Biomni ecosystem.
|
| 509 |
+
|
| 510 |
+
If you have particular tool/database/software in mind that you want to add, you can also submit to [this form](https://forms.gle/nu2n1unzAYodTLVj6) and the biomni team will implement them.
|
| 511 |
+
|
| 512 |
+
## 🔬 Call for Contributors: Help Build Biomni-E2
|
| 513 |
+
|
| 514 |
+
Biomni-E1 only scratches the surface of what’s possible in the biomedical action space.
|
| 515 |
+
|
| 516 |
+
Now, we’re building **Biomni-E2** — a next-generation environment developed **with and for the community**.
|
| 517 |
+
|
| 518 |
+
We believe that by collaboratively defining and curating a shared library of standard biomedical actions, we can accelerate science for everyone.
|
| 519 |
+
|
| 520 |
+
**Join us in shaping the future of biomedical AI agent.**
|
| 521 |
+
|
| 522 |
+
- **Contributors with significant impact** (e.g., 10+ significant & integrated tool contributions or equivalent) will be **invited as co-authors** on our upcoming paper in a top-tier journal or conference.
|
| 523 |
+
- **All contributors** will be acknowledged in our publications.
|
| 524 |
+
- More contributor perks...
|
| 525 |
+
|
| 526 |
+
Let’s build it together.
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
## Tutorials and Examples
|
| 530 |
+
|
| 531 |
+
**[Biomni 101](./tutorials/biomni_101.ipynb)** - Basic concepts and first steps
|
| 532 |
+
|
| 533 |
+
More to come!
|
| 534 |
+
|
| 535 |
+
## 🌐 Web Interface
|
| 536 |
+
|
| 537 |
+
Experience Biomni through our no-code web interface at **[biomni.stanford.edu](https://biomni.stanford.edu)**.
|
| 538 |
+
|
| 539 |
+
[](https://youtu.be/E0BRvl23hLs)
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
## Important Note
|
| 543 |
+
- Security warning: Currently, Biomni executes LLM-generated code with full system privileges. If you want to use it in production, please use in isolated/sandboxed environments. The agent can access files, network, and system commands. Be careful with sensitive data or credentials.
|
| 544 |
+
- This release was frozen as of April 15 2025, so it differs from the current web platform.
|
| 545 |
+
- Biomni itself is Apache 2.0-licensed, but certain integrated tools, databases, or software may carry more restrictive commercial licenses. Review each component carefully before any commercial use.
|
| 546 |
+
|
| 547 |
+
## Cite Us
|
| 548 |
+
|
| 549 |
+
```
|
| 550 |
+
@article{huang2025biomni,
|
| 551 |
+
title={Biomni: A General-Purpose Biomedical AI Agent},
|
| 552 |
+
author={Huang, Kexin and Zhang, Serena and Wang, Hanchen and Qu, Yuanhao and Lu, Yingzhou and Roohani, Yusuf and Li, Ryan and Qiu, Lin and Zhang, Junze and Di, Yin and others},
|
| 553 |
+
journal={bioRxiv},
|
| 554 |
+
pages={2025--05},
|
| 555 |
+
year={2025},
|
| 556 |
+
publisher={Cold Spring Harbor Laboratory}
|
| 557 |
+
}
|
| 558 |
+
```
|
Biomanus/biomni/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .version import __version__
|
| 2 |
+
|
| 3 |
+
__all__ = ["__version__"]
|
Biomanus/biomni/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (207 Bytes). View file
|
|
|
Biomanus/biomni/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (240 Bytes). View file
|
|
|
Biomanus/biomni/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (219 Bytes). View file
|
|
|
Biomanus/biomni/__pycache__/config.cpython-310.pyc
ADDED
|
Binary file (3.26 kB). View file
|
|
|
Biomanus/biomni/__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (6.06 kB). View file
|
|
|
Biomanus/biomni/__pycache__/config.cpython-313.pyc
ADDED
|
Binary file (6.22 kB). View file
|
|
|
Biomanus/biomni/__pycache__/env_desc.cpython-310.pyc
ADDED
|
Binary file (22.6 kB). View file
|
|
|
Biomanus/biomni/__pycache__/env_desc.cpython-311.pyc
ADDED
|
Binary file (24.9 kB). View file
|
|
|
Biomanus/biomni/__pycache__/llm.cpython-310.pyc
ADDED
|
Binary file (6.68 kB). View file
|
|
|
Biomanus/biomni/__pycache__/llm.cpython-311.pyc
ADDED
|
Binary file (10.4 kB). View file
|
|
|
Biomanus/biomni/__pycache__/llm.cpython-313.pyc
ADDED
|
Binary file (9.39 kB). View file
|
|
|
Biomanus/biomni/__pycache__/utils.cpython-310.pyc
ADDED
|
Binary file (66.5 kB). View file
|
|
|
Biomanus/biomni/__pycache__/utils.cpython-313.pyc
ADDED
|
Binary file (93.1 kB). View file
|
|
|
Biomanus/biomni/__pycache__/version.cpython-310.pyc
ADDED
|
Binary file (162 Bytes). View file
|
|
|
Biomanus/biomni/__pycache__/version.cpython-311.pyc
ADDED
|
Binary file (177 Bytes). View file
|
|
|
Biomanus/biomni/__pycache__/version.cpython-313.pyc
ADDED
|
Binary file (171 Bytes). View file
|
|
|
Biomanus/biomni/agent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from biomni.agent.a1 import A1 # noqa: F401
|
Biomanus/biomni/agent/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (190 Bytes). View file
|
|
|
Biomanus/biomni/agent/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (220 Bytes). View file
|
|
|
Biomanus/biomni/agent/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (197 Bytes). View file
|
|
|
Biomanus/biomni/agent/__pycache__/a1.cpython-310.pyc
ADDED
|
Binary file (99.6 kB). View file
|
|
|
Biomanus/biomni/agent/a1.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Biomanus/biomni/agent/env_collection.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import re
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 7 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 8 |
+
|
| 9 |
+
from biomni.agent.base_agent import base_agent
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class PaperTaskExtractor(base_agent):
|
| 13 |
+
"""Agent that extracts generalizable tasks/experiments from academic papers.
|
| 14 |
+
It processes papers in chunks and identifies common research tasks that could be shared across papers.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
def __init__(
|
| 18 |
+
self,
|
| 19 |
+
llm="claude-3-7-sonnet-20250219",
|
| 20 |
+
cheap_llm=None,
|
| 21 |
+
tools=None,
|
| 22 |
+
chunk_size=4000,
|
| 23 |
+
chunk_overlap=400,
|
| 24 |
+
):
|
| 25 |
+
"""Initialize the PaperTaskExtractor agent.
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
llm (str): The LLM model to use
|
| 29 |
+
cheap_llm (str, optional): A cheaper LLM for simpler tasks
|
| 30 |
+
tools (list, optional): Any tools to use (not needed for this agent)
|
| 31 |
+
chunk_size (int): Size of text chunks for processing
|
| 32 |
+
chunk_overlap (int): Overlap between chunks
|
| 33 |
+
|
| 34 |
+
"""
|
| 35 |
+
super().__init__(llm, cheap_llm, tools)
|
| 36 |
+
self.chunk_size = chunk_size
|
| 37 |
+
self.chunk_overlap = chunk_overlap
|
| 38 |
+
self.log = []
|
| 39 |
+
self.configure()
|
| 40 |
+
|
| 41 |
+
def configure(self):
|
| 42 |
+
"""Configure the agent with appropriate prompts."""
|
| 43 |
+
# Prompt for analyzing paper chunks
|
| 44 |
+
self.chunk_analysis_prompt = """You are a research methodology expert specializing in identifying computational tasks and data analysis procedures in academic papers.
|
| 45 |
+
|
| 46 |
+
Your job is to analyze chunks of academic papers and identify ONLY the most common, generalizable computational tasks that are widely used across biomedical research and can be implemented with Python or Linux code.
|
| 47 |
+
|
| 48 |
+
STRICT GUIDELINES:
|
| 49 |
+
1. ONLY extract tasks that are extremely common and standard in computational biomedical research
|
| 50 |
+
2. Each task MUST have clear, well-defined inputs and outputs
|
| 51 |
+
3. Tasks MUST be generalizable across many different papers and research questions
|
| 52 |
+
4. Be VERY selective - only include tasks that appear in hundreds of papers
|
| 53 |
+
5. If a task is specific to this paper, unclear, or not widely used, DO NOT include it
|
| 54 |
+
6. Focus on computational tasks that can be implemented with Python or Linux code
|
| 55 |
+
7. Each task should be something that could be implemented as a function with clear inputs/outputs
|
| 56 |
+
8. Also identify commonly used databases and software packages mentioned in the text
|
| 57 |
+
9. Tasks MUST be CONCRETE and SPECIFIC - include exact methodological details
|
| 58 |
+
10. Avoid vague task names like "Statistical Analysis" - instead use specific protocol names like "Two-way ANOVA with Tukey's Post-hoc Test using SciPy"
|
| 59 |
+
11. DO NOT include wet lab procedures that cannot be implemented computationally
|
| 60 |
+
12. ONLY include tasks that could be automated with code
|
| 61 |
+
|
| 62 |
+
For the following chunk of text from an academic paper, provide:
|
| 63 |
+
1. A list of ONLY the most common, generalizable COMPUTATIONAL tasks identified (be extremely selective)
|
| 64 |
+
2. For each task, clearly define:
|
| 65 |
+
- Task name: A SPECIFIC and CONCRETE name with methodological details (e.g., "RNA-seq Differential Expression Analysis with DESeq2" instead of just "Gene Expression Analysis")
|
| 66 |
+
- Input: What SPECIFIC data or parameters the task requires
|
| 67 |
+
- Output: What SPECIFIC data or results the task produces
|
| 68 |
+
- Code implementation: How this task could be implemented with Python or Linux code, including key libraries/packages
|
| 69 |
+
- Frequency: How common this computational task is in biomedical research
|
| 70 |
+
- Standard methods: The established computational techniques used to perform this task
|
| 71 |
+
- Example: A brief description of how THIS specific paper uses this task (with specific details from the paper)
|
| 72 |
+
3. A list of commonly used databases mentioned in the text (if any)
|
| 73 |
+
4. A list of commonly used software packages/tools mentioned in the text (if any)
|
| 74 |
+
|
| 75 |
+
PAPER CHUNK:
|
| 76 |
+
{chunk_text}
|
| 77 |
+
|
| 78 |
+
Remember, it's better to return NO tasks than to include tasks that aren't extremely common, generalizable, and implementable with code. Quality over quantity is essential. Tasks MUST be CONCRETE with SPECIFIC methodological details and MUST be implementable with Python or Linux code.
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
# Prompt for consolidating tasks
|
| 82 |
+
self.consolidation_prompt = """You are a research methodology expert. Your task is to consolidate lists of computational research tasks extracted from different chunks of an academic paper.
|
| 83 |
+
|
| 84 |
+
BE EXTREMELY SELECTIVE. Only include tasks that are:
|
| 85 |
+
1. Fundamental to computational biomedical research
|
| 86 |
+
2. Used in hundreds of papers across different subfields
|
| 87 |
+
3. Have clear, well-defined inputs and outputs
|
| 88 |
+
4. Represent standard computational approaches
|
| 89 |
+
5. Could be implemented as a function with specific inputs and outputs
|
| 90 |
+
6. Are CONCRETE and SPECIFIC with exact methodological details
|
| 91 |
+
7. Can be implemented with Python or Linux code
|
| 92 |
+
8. Are computational in nature, not wet lab procedures
|
| 93 |
+
|
| 94 |
+
REMOVE any tasks that:
|
| 95 |
+
- Are specific to a particular paper or dataset
|
| 96 |
+
- Lack clear inputs or outputs
|
| 97 |
+
- Are not widely used across biomedical research
|
| 98 |
+
- Are vague or poorly defined
|
| 99 |
+
- Represent niche or specialized techniques
|
| 100 |
+
- Have generic names without specific methodological details
|
| 101 |
+
- Cannot be implemented with code
|
| 102 |
+
- Require physical lab equipment or manual intervention
|
| 103 |
+
|
| 104 |
+
The output should be a JSON object with three main keys:
|
| 105 |
+
1. "tasks": A list of task objects, where each task has:
|
| 106 |
+
- "task_name": A SPECIFIC and CONCRETE name with methodological details (e.g., "Single-cell RNA-seq Clustering with Seurat" instead of just "Cell Clustering")
|
| 107 |
+
- "description": A clear description of what the computational task does
|
| 108 |
+
- "inputs": SPECIFIC data types or parameters the task requires
|
| 109 |
+
- "outputs": SPECIFIC data types or results the task produces
|
| 110 |
+
- "code_implementation": How this task could be implemented with Python or Linux code, including key libraries/packages and a brief pseudocode example
|
| 111 |
+
- "frequency": How common this computational task is in biomedical research
|
| 112 |
+
- "standard_methods": The established computational techniques used to perform this task
|
| 113 |
+
- "example": A specific example from THIS paper showing how the task was used (with concrete details)
|
| 114 |
+
|
| 115 |
+
2. "databases": A list of database objects, where each database has:
|
| 116 |
+
- "name": The name of the database
|
| 117 |
+
- "description": A brief description of what the database contains
|
| 118 |
+
- "url": The URL of the database (if mentioned)
|
| 119 |
+
- "usage": How the database is commonly used in computational biomedical research
|
| 120 |
+
- "example": How this specific paper uses the database
|
| 121 |
+
|
| 122 |
+
3. "software": A list of software package objects, where each package has:
|
| 123 |
+
- "name": The name of the software package
|
| 124 |
+
- "description": A brief description of what the software does
|
| 125 |
+
- "url": The URL or reference to the software (if mentioned)
|
| 126 |
+
- "usage": How the software is commonly used in computational biomedical research
|
| 127 |
+
- "example": How this specific paper uses the software
|
| 128 |
+
|
| 129 |
+
EXTRACTED INFORMATION FROM PAPER CHUNKS:
|
| 130 |
+
{task_lists}
|
| 131 |
+
|
| 132 |
+
Be ruthless in filtering - it's better to return a few truly common computational tasks than many that aren't universal or implementable with code.
|
| 133 |
+
Respond with only a valid JSON object containing the three lists described above.
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
def process_paper(self, paper_text: str) -> dict[str, list[dict[str, Any]]]:
|
| 137 |
+
"""Process a paper and extract generalizable tasks/experiments, databases, and software.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
paper_text (str): The full text of the paper
|
| 141 |
+
|
| 142 |
+
Returns:
|
| 143 |
+
Dict[str, List[Dict[str, Any]]]: A dictionary with tasks, databases, and software
|
| 144 |
+
|
| 145 |
+
"""
|
| 146 |
+
# Split the paper into chunks
|
| 147 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
| 148 |
+
chunk_size=self.chunk_size,
|
| 149 |
+
chunk_overlap=self.chunk_overlap,
|
| 150 |
+
length_function=len,
|
| 151 |
+
separators=["\n\n", "\n", ". ", " ", ""],
|
| 152 |
+
)
|
| 153 |
+
chunks = text_splitter.split_text(paper_text)
|
| 154 |
+
|
| 155 |
+
# Process each chunk to extract tasks
|
| 156 |
+
chunk_results = []
|
| 157 |
+
for i, chunk in enumerate(chunks):
|
| 158 |
+
print(f"Processing chunk {i + 1}/{len(chunks)}...")
|
| 159 |
+
chunk_tasks = self._process_chunk(chunk)
|
| 160 |
+
chunk_results.append(chunk_tasks)
|
| 161 |
+
|
| 162 |
+
# Consolidate tasks from all chunks
|
| 163 |
+
consolidated_results = self._consolidate_tasks(chunk_results)
|
| 164 |
+
return consolidated_results
|
| 165 |
+
|
| 166 |
+
def _process_chunk(self, chunk_text: str) -> str:
|
| 167 |
+
"""Process a single chunk of the paper to extract tasks.
|
| 168 |
+
|
| 169 |
+
Args:
|
| 170 |
+
chunk_text (str): The chunk text to process
|
| 171 |
+
|
| 172 |
+
Returns:
|
| 173 |
+
str: Extracted tasks from this chunk
|
| 174 |
+
|
| 175 |
+
"""
|
| 176 |
+
prompt = self.chunk_analysis_prompt.format(chunk_text=chunk_text)
|
| 177 |
+
message = self.llm.invoke(prompt)
|
| 178 |
+
return message.content
|
| 179 |
+
|
| 180 |
+
def _consolidate_tasks(self, chunk_results: list[str]) -> dict[str, list[dict[str, Any]]]:
|
| 181 |
+
"""Consolidate tasks, databases, and software extracted from different chunks into a unified structure.
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
chunk_results (List[str]): Results from each chunk
|
| 185 |
+
|
| 186 |
+
Returns:
|
| 187 |
+
Dict[str, List[Dict[str, Any]]]: Consolidated information with tasks, databases, and software
|
| 188 |
+
|
| 189 |
+
"""
|
| 190 |
+
# Combine all chunk results
|
| 191 |
+
all_tasks = "\n\n===== CHUNK SEPARATOR =====\n\n".join(chunk_results)
|
| 192 |
+
|
| 193 |
+
# Use the consolidation prompt to merge and organize tasks
|
| 194 |
+
prompt = self.consolidation_prompt.format(task_lists=all_tasks)
|
| 195 |
+
response = self.llm.invoke(prompt)
|
| 196 |
+
|
| 197 |
+
# Extract the JSON from the response
|
| 198 |
+
try:
|
| 199 |
+
# Try to parse the entire response as JSON
|
| 200 |
+
result = json.loads(response.content)
|
| 201 |
+
except json.JSONDecodeError:
|
| 202 |
+
# If that fails, try to extract JSON from the text
|
| 203 |
+
try:
|
| 204 |
+
# Look for JSON-like content between triple backticks
|
| 205 |
+
json_match = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", response.content)
|
| 206 |
+
if json_match:
|
| 207 |
+
result = json.loads(json_match.group(1))
|
| 208 |
+
else:
|
| 209 |
+
# Fallback: just return the text response
|
| 210 |
+
return {
|
| 211 |
+
"tasks": [
|
| 212 |
+
{
|
| 213 |
+
"error": "Could not parse JSON",
|
| 214 |
+
"raw_response": response.content,
|
| 215 |
+
}
|
| 216 |
+
],
|
| 217 |
+
"databases": [],
|
| 218 |
+
"software": [],
|
| 219 |
+
}
|
| 220 |
+
except Exception:
|
| 221 |
+
return {
|
| 222 |
+
"tasks": [
|
| 223 |
+
{
|
| 224 |
+
"error": "Could not parse JSON",
|
| 225 |
+
"raw_response": response.content,
|
| 226 |
+
}
|
| 227 |
+
],
|
| 228 |
+
"databases": [],
|
| 229 |
+
"software": [],
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
# Ensure the result has the expected structure
|
| 233 |
+
if not isinstance(result, dict):
|
| 234 |
+
result = {
|
| 235 |
+
"tasks": result if isinstance(result, list) else [],
|
| 236 |
+
"databases": [],
|
| 237 |
+
"software": [],
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
# Ensure all required keys exist
|
| 241 |
+
for key in ["tasks", "databases", "software"]:
|
| 242 |
+
if key not in result:
|
| 243 |
+
result[key] = []
|
| 244 |
+
|
| 245 |
+
return result
|
| 246 |
+
|
| 247 |
+
def go(self, paper_text: str):
|
| 248 |
+
"""Process a paper and return the extracted tasks, databases, and software.
|
| 249 |
+
|
| 250 |
+
Args:
|
| 251 |
+
paper_text (str): The full text of the paper
|
| 252 |
+
|
| 253 |
+
Returns:
|
| 254 |
+
tuple: (log, results) where log is a list of processing steps and results is the final result
|
| 255 |
+
|
| 256 |
+
"""
|
| 257 |
+
self.log = []
|
| 258 |
+
self.log.append(
|
| 259 |
+
(
|
| 260 |
+
"user",
|
| 261 |
+
"Extract only the most common and generalizable biomedical research tasks, databases, and software from this paper",
|
| 262 |
+
)
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
results = self.process_paper(paper_text)
|
| 266 |
+
|
| 267 |
+
result_str = json.dumps(results, indent=2)
|
| 268 |
+
self.log.append(("assistant", result_str))
|
| 269 |
+
|
| 270 |
+
return self.log, results
|
| 271 |
+
|
| 272 |
+
def save_results(self, results: dict[str, list[dict[str, Any]]], output_path: str):
|
| 273 |
+
"""Save the extracted tasks, databases, and software to a JSON file.
|
| 274 |
+
|
| 275 |
+
Args:
|
| 276 |
+
results (Dict[str, List[Dict[str, Any]]]): The extracted tasks, databases, and software
|
| 277 |
+
output_path (str): Path to save the results
|
| 278 |
+
|
| 279 |
+
"""
|
| 280 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 281 |
+
with open(output_path, "w") as f:
|
| 282 |
+
json.dump(results, f, indent=2)
|
| 283 |
+
print(f"Results saved to {output_path}")
|
| 284 |
+
|
| 285 |
+
def result_formatting(self, output_class, task_intention):
|
| 286 |
+
"""Format the results according to a specific output class.
|
| 287 |
+
|
| 288 |
+
Args:
|
| 289 |
+
output_class: The class to format the output as
|
| 290 |
+
task_intention: Description of the task
|
| 291 |
+
|
| 292 |
+
Returns:
|
| 293 |
+
The formatted result
|
| 294 |
+
|
| 295 |
+
"""
|
| 296 |
+
format_check_prompt = ChatPromptTemplate.from_messages(
|
| 297 |
+
[
|
| 298 |
+
(
|
| 299 |
+
"system",
|
| 300 |
+
(
|
| 301 |
+
"You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
|
| 302 |
+
"Review the entire history of messages provided. "
|
| 303 |
+
"Here is the task output requirement: \n"
|
| 304 |
+
f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
|
| 305 |
+
),
|
| 306 |
+
),
|
| 307 |
+
("placeholder", "{messages}"),
|
| 308 |
+
]
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
checker_llm = format_check_prompt | self.llm.with_structured_output(output_class)
|
| 312 |
+
result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
|
| 313 |
+
return result
|
Biomanus/biomni/agent/function_generator.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
from biomni.llm import get_llm
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class base_agent:
|
| 7 |
+
def __init__(self, llm="claude-3-haiku-20240307", cheap_llm=None, tools=None, temperature=0.7):
|
| 8 |
+
self.tools = tools
|
| 9 |
+
self.llm = get_llm(llm, temperature)
|
| 10 |
+
if cheap_llm is None:
|
| 11 |
+
self.cheap_llm = llm
|
| 12 |
+
else:
|
| 13 |
+
self.cheap_llm = cheap_llm
|
| 14 |
+
|
| 15 |
+
def configure(self):
|
| 16 |
+
pass
|
| 17 |
+
|
| 18 |
+
def go(self, input):
|
| 19 |
+
pass
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class FunctionGenerator(base_agent):
|
| 23 |
+
"""Agent that generates executable Python code scripts given a task description."""
|
| 24 |
+
|
| 25 |
+
def __init__(self, llm="claude-3-7-sonnet-20250219", cheap_llm=None, temperature=0.7):
|
| 26 |
+
"""Initialize the PaperTaskExtractor agent.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
llm (str): The LLM model to use
|
| 30 |
+
cheap_llm (str, optional): A cheaper LLM for simpler tasks
|
| 31 |
+
"""
|
| 32 |
+
super().__init__(llm, cheap_llm, temperature)
|
| 33 |
+
self.log = []
|
| 34 |
+
self.configure()
|
| 35 |
+
|
| 36 |
+
def configure(self):
|
| 37 |
+
"""Configure the agent with appropriate prompts."""
|
| 38 |
+
# Prompt for Python code generation
|
| 39 |
+
self.system_prompt = """You are a senior Python engineer. Generate robust, idiomatic Python code that solves the user's task. Requirements:
|
| 40 |
+
1. Output ONLY Python code, ideally inside a single triple-backtick code block.
|
| 41 |
+
2. Include minimal inline comments and a small docstring.
|
| 42 |
+
3. Add a `main()` and an `if __name__ == '__main__':` guard when appropriate.
|
| 43 |
+
4. Avoid external dependencies unless necessary; if used, show `pip` installs in comments.
|
| 44 |
+
5. Do not include prose before or after the code.
|
| 45 |
+
6. When applicable, prioritize the use of codes on public repositories, such as HuggingFace or Github
|
| 46 |
+
|
| 47 |
+
Generate Python codes for the following task:
|
| 48 |
+
{task}
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
def _generate_code(self, task_description: str) -> str:
|
| 52 |
+
"""Generate codes given a task description.
|
| 53 |
+
Args:
|
| 54 |
+
task_description (str): task descriptions (possibly generated from previous steps)
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
str: generated code string
|
| 58 |
+
|
| 59 |
+
"""
|
| 60 |
+
prompt = self.system_prompt.format(task=task_description)
|
| 61 |
+
message = self.llm.invoke(prompt)
|
| 62 |
+
return message.content
|
| 63 |
+
|
| 64 |
+
def _generate_script_filename(self, task_description: str, max_words: int = 6) -> str:
|
| 65 |
+
"""
|
| 66 |
+
Generate a safe, meaningful Python script filename from a task description.
|
| 67 |
+
Poised for update: may ask the agent to suggest meaningful names.
|
| 68 |
+
|
| 69 |
+
Parameters:
|
| 70 |
+
-----------
|
| 71 |
+
task_description (str): task descriptions (possibly generated from previous steps)
|
| 72 |
+
|
| 73 |
+
max_words : int
|
| 74 |
+
Maximum number of words to include in the filename.
|
| 75 |
+
|
| 76 |
+
Returns:
|
| 77 |
+
--------
|
| 78 |
+
str
|
| 79 |
+
A lowercase, hyphen-free, safe filename ending in '.py'.
|
| 80 |
+
"""
|
| 81 |
+
# Lowercase and remove non-alphanumeric (allow spaces for splitting)
|
| 82 |
+
cleaned = re.sub(r"[^a-zA-Z0-9\s]", "", task_description.lower())
|
| 83 |
+
|
| 84 |
+
# Tokenize and select top words
|
| 85 |
+
words = cleaned.split()
|
| 86 |
+
selected_words = words[:max_words] if words else ["script"]
|
| 87 |
+
|
| 88 |
+
# Join with underscores
|
| 89 |
+
base_name = "_".join(selected_words)
|
| 90 |
+
return f"{base_name}.py"
|
| 91 |
+
|
| 92 |
+
def go(self, task_description: str):
|
| 93 |
+
"""Implement the inherited function to get the tasks done.
|
| 94 |
+
|
| 95 |
+
Args:
|
| 96 |
+
task_description (str): task descriptions (possibly generated from previous steps)
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
tuple: (script_filename, results) where script_filename is a generated name for script file and results is the generated codes
|
| 100 |
+
|
| 101 |
+
"""
|
| 102 |
+
self.log = []
|
| 103 |
+
self.log.append(
|
| 104 |
+
(
|
| 105 |
+
"user",
|
| 106 |
+
"Generate Python codes given a task description",
|
| 107 |
+
)
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
script_filename = self._generate_script_filename(task_description)
|
| 111 |
+
results = self._generate_code(task_description)
|
| 112 |
+
return script_filename, self._extract_code_block(results)
|
| 113 |
+
|
| 114 |
+
def _extract_code_block(self, s: str) -> str:
|
| 115 |
+
"""
|
| 116 |
+
Extract the first fenced code block (``` or ```python) from s.
|
| 117 |
+
"""
|
| 118 |
+
m = re.search(r"```(?:python)?\s*(.+?)\s*```", s, flags=re.DOTALL | re.IGNORECASE)
|
| 119 |
+
return m.group(1).strip() if m else None
|
Biomanus/biomni/agent/qa_llm.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 2 |
+
|
| 3 |
+
from biomni.llm import get_llm
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class qa_llm:
|
| 7 |
+
def __init__(self, path="./data", llm="claude-3-haiku-20240307", lab_bench_reproduce=False):
|
| 8 |
+
self.path = path
|
| 9 |
+
self.llm = get_llm(llm)
|
| 10 |
+
|
| 11 |
+
if lab_bench_reproduce:
|
| 12 |
+
self.prompt_modifier = """
|
| 13 |
+
The following is a multiple choice question about biology.
|
| 14 |
+
Please answer by responding with the letter of the correct answer.
|
| 15 |
+
|
| 16 |
+
Think step by step. \n
|
| 17 |
+
"""
|
| 18 |
+
else:
|
| 19 |
+
self.prompt_modifier = ""
|
| 20 |
+
self.log = []
|
| 21 |
+
|
| 22 |
+
def configure(self):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
def go(self, input):
|
| 26 |
+
self.log = []
|
| 27 |
+
self.log.append(("user", input))
|
| 28 |
+
message = self.llm.invoke(self.prompt_modifier + input)
|
| 29 |
+
self.log.append(("assistant", message.content))
|
| 30 |
+
return [message.content], message.content
|
| 31 |
+
|
| 32 |
+
def result_formatting(self, output_class, task_intention):
|
| 33 |
+
self.format_check_prompt = ChatPromptTemplate.from_messages(
|
| 34 |
+
[
|
| 35 |
+
(
|
| 36 |
+
"system",
|
| 37 |
+
(
|
| 38 |
+
"You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
|
| 39 |
+
"Review the entire history of messages provided. "
|
| 40 |
+
"Here is the task output requirement: \n"
|
| 41 |
+
f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
|
| 42 |
+
),
|
| 43 |
+
),
|
| 44 |
+
("placeholder", "{messages}"),
|
| 45 |
+
]
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
checker_llm = self.format_check_prompt | self.llm.with_structured_output(output_class)
|
| 49 |
+
result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
|
| 50 |
+
return result
|
Biomanus/biomni/agent/react.py
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import glob
|
| 2 |
+
import inspect
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
import signal
|
| 6 |
+
from collections.abc import Sequence
|
| 7 |
+
from functools import wraps
|
| 8 |
+
from multiprocessing import Process, Queue
|
| 9 |
+
from typing import Annotated, TypedDict
|
| 10 |
+
|
| 11 |
+
from langchain_core.messages import BaseMessage, SystemMessage, ToolMessage
|
| 12 |
+
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
| 13 |
+
from langchain_core.runnables import RunnableConfig
|
| 14 |
+
from langgraph.graph import END, StateGraph
|
| 15 |
+
from langgraph.graph.message import add_messages
|
| 16 |
+
|
| 17 |
+
from biomni.config import default_config
|
| 18 |
+
from biomni.env_desc import data_lake_dict, library_content_dict
|
| 19 |
+
from biomni.llm import get_llm
|
| 20 |
+
from biomni.model.retriever import ToolRetriever
|
| 21 |
+
from biomni.tool.tool_registry import ToolRegistry
|
| 22 |
+
from biomni.utils import (
|
| 23 |
+
api_schema_to_langchain_tool,
|
| 24 |
+
function_to_api_schema,
|
| 25 |
+
pretty_print,
|
| 26 |
+
read_module2api,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
# Define the AgentState TypedDict for our custom implementation
|
| 31 |
+
class AgentState(TypedDict):
|
| 32 |
+
"""The state of the agent."""
|
| 33 |
+
|
| 34 |
+
# add_messages is a reducer that combines message sequences
|
| 35 |
+
messages: Annotated[Sequence[BaseMessage], add_messages]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class react:
|
| 39 |
+
def __init__(
|
| 40 |
+
self,
|
| 41 |
+
path: str | None = None,
|
| 42 |
+
llm: str | None = None,
|
| 43 |
+
use_tool_retriever: bool | None = None,
|
| 44 |
+
timeout_seconds: int | None = None,
|
| 45 |
+
):
|
| 46 |
+
# Use default_config values for unspecified parameters
|
| 47 |
+
if path is None:
|
| 48 |
+
path = default_config.path
|
| 49 |
+
if llm is None:
|
| 50 |
+
llm = default_config.llm
|
| 51 |
+
if use_tool_retriever is None:
|
| 52 |
+
use_tool_retriever = default_config.use_tool_retriever
|
| 53 |
+
if timeout_seconds is None:
|
| 54 |
+
timeout_seconds = default_config.timeout_seconds
|
| 55 |
+
|
| 56 |
+
self.path = path
|
| 57 |
+
if not os.path.exists(path):
|
| 58 |
+
os.makedirs(path)
|
| 59 |
+
print(f"Created directory: {path}")
|
| 60 |
+
### TODO: Download the data
|
| 61 |
+
else:
|
| 62 |
+
print(f"Data directory already exists: {path}, loading...")
|
| 63 |
+
|
| 64 |
+
module2api = read_module2api()
|
| 65 |
+
|
| 66 |
+
self.llm = get_llm(llm, config=default_config)
|
| 67 |
+
tools = []
|
| 68 |
+
for module, api_list in module2api.items():
|
| 69 |
+
print("Registering tools from module:", module)
|
| 70 |
+
tools += [api_schema_to_langchain_tool(api, mode="custom_tool", module_name=module) for api in api_list]
|
| 71 |
+
self.tools = tools
|
| 72 |
+
self.module2api = module2api
|
| 73 |
+
self.use_tool_retriever = use_tool_retriever
|
| 74 |
+
|
| 75 |
+
# Store dictionaries for data lake and library content
|
| 76 |
+
self.data_lake_dict = data_lake_dict
|
| 77 |
+
self.library_content_dict = library_content_dict
|
| 78 |
+
|
| 79 |
+
if self.use_tool_retriever:
|
| 80 |
+
self.tool_registry = ToolRegistry(module2api)
|
| 81 |
+
self.retriever = ToolRetriever()
|
| 82 |
+
|
| 83 |
+
self.timeout_seconds = timeout_seconds # 10 minutes default timeout
|
| 84 |
+
|
| 85 |
+
# When wrapping tools with timeout
|
| 86 |
+
self.tools = self._add_timeout_to_tools(self.tools)
|
| 87 |
+
self.prompt = ""
|
| 88 |
+
self.system_prompt = ""
|
| 89 |
+
|
| 90 |
+
def _add_timeout_to_tools(self, tools):
|
| 91 |
+
"""Apply timeout wrapper to all tool functions using multiprocessing."""
|
| 92 |
+
|
| 93 |
+
def create_timed_func(original_func, timeout):
|
| 94 |
+
"""Factory function that creates a unique timed function for each tool."""
|
| 95 |
+
tool_name = getattr(original_func, "__name__", "unknown")
|
| 96 |
+
# print(f"Applying timeout wrapper to tool: {tool_name}")
|
| 97 |
+
|
| 98 |
+
def process_func(func, args, kwargs, result_queue):
|
| 99 |
+
"""Function to run in a separate process."""
|
| 100 |
+
try:
|
| 101 |
+
result = func(*args, **kwargs)
|
| 102 |
+
result_queue.put(("success", result))
|
| 103 |
+
except Exception as e:
|
| 104 |
+
result_queue.put(("error", str(e)))
|
| 105 |
+
|
| 106 |
+
@wraps(original_func)
|
| 107 |
+
def timed_func(*args, **kwargs):
|
| 108 |
+
# print(f"Executing tool with timeout: {tool_name}")
|
| 109 |
+
result_queue = Queue()
|
| 110 |
+
|
| 111 |
+
# Start a separate process
|
| 112 |
+
proc = Process(
|
| 113 |
+
target=process_func,
|
| 114 |
+
args=(original_func, args, kwargs, result_queue),
|
| 115 |
+
)
|
| 116 |
+
proc.start()
|
| 117 |
+
|
| 118 |
+
# Wait for the specified timeout
|
| 119 |
+
proc.join(timeout)
|
| 120 |
+
|
| 121 |
+
# Check if the process is still running after timeout
|
| 122 |
+
if proc.is_alive():
|
| 123 |
+
print(f"TIMEOUT: Tool {tool_name} execution timed out after {timeout} seconds")
|
| 124 |
+
# Force terminate the process
|
| 125 |
+
proc.terminate()
|
| 126 |
+
proc.join(1) # Give it a second to terminate
|
| 127 |
+
|
| 128 |
+
# If it's still not dead, kill it with more force
|
| 129 |
+
if proc.is_alive():
|
| 130 |
+
os.kill(proc.pid, signal.SIGKILL)
|
| 131 |
+
|
| 132 |
+
return f"ERROR: Tool execution timed out after {timeout} seconds. Please try with simpler inputs or break your task into smaller steps."
|
| 133 |
+
|
| 134 |
+
# Get the result from the queue
|
| 135 |
+
if not result_queue.empty():
|
| 136 |
+
status, result = result_queue.get()
|
| 137 |
+
if status == "success":
|
| 138 |
+
return result
|
| 139 |
+
else:
|
| 140 |
+
return f"Error in tool execution: {result}"
|
| 141 |
+
|
| 142 |
+
return "Error: Tool execution completed but no result was returned"
|
| 143 |
+
|
| 144 |
+
return timed_func
|
| 145 |
+
|
| 146 |
+
wrapped_tools = []
|
| 147 |
+
for tool in tools:
|
| 148 |
+
wrapped_tool = tool
|
| 149 |
+
wrapped_tool.func = create_timed_func(tool.func, self.timeout_seconds)
|
| 150 |
+
wrapped_tools.append(wrapped_tool)
|
| 151 |
+
|
| 152 |
+
return wrapped_tools
|
| 153 |
+
|
| 154 |
+
def add_tool(self, api):
|
| 155 |
+
function_code = inspect.getsource(api)
|
| 156 |
+
schema = function_to_api_schema(function_code, self.llm)
|
| 157 |
+
new_tool = api_schema_to_langchain_tool(schema, mode="custom_tool", module_name=api.__module__)
|
| 158 |
+
|
| 159 |
+
# Create a single wrapped tool using the existing _add_timeout_to_tools method
|
| 160 |
+
wrapped_tools = self._add_timeout_to_tools([new_tool])
|
| 161 |
+
|
| 162 |
+
# Get the wrapped tool and add it to our tools list
|
| 163 |
+
if wrapped_tools:
|
| 164 |
+
self.tools.append(wrapped_tools[0])
|
| 165 |
+
|
| 166 |
+
def configure(
|
| 167 |
+
self,
|
| 168 |
+
plan=False,
|
| 169 |
+
reflect=False,
|
| 170 |
+
data_lake=False,
|
| 171 |
+
react_code_search=False,
|
| 172 |
+
library_access=False,
|
| 173 |
+
):
|
| 174 |
+
data_lake_path = self.path + "/data_lake"
|
| 175 |
+
data_lake_content = glob.glob(data_lake_path + "/*")
|
| 176 |
+
data_lake_items = [x.split("/")[-1] for x in data_lake_content]
|
| 177 |
+
|
| 178 |
+
if react_code_search:
|
| 179 |
+
tools = [i for i in self.tools if i.name in ["run_python_repl", "search_google"]]
|
| 180 |
+
|
| 181 |
+
prompt_modifier = """
|
| 182 |
+
You are a helpful biomedical assistant assigned with the task of problem-solving.
|
| 183 |
+
|
| 184 |
+
You have access to two tools:
|
| 185 |
+
1) run_python_repl: to write and run python code
|
| 186 |
+
2) search_google: to search google for information
|
| 187 |
+
|
| 188 |
+
You can use them to solve the problem.
|
| 189 |
+
"""
|
| 190 |
+
else:
|
| 191 |
+
tools = self.tools
|
| 192 |
+
if (not plan) and (not reflect):
|
| 193 |
+
prompt_modifier = """You are a helpful biologist and expert geneticist.
|
| 194 |
+
"""
|
| 195 |
+
elif plan and (not reflect):
|
| 196 |
+
prompt_modifier = """You are a helpful biologist and expert geneticist.
|
| 197 |
+
Given the question from the user,
|
| 198 |
+
- First, come up with a high level plan based on your understanding of the problem and available tools and record it in the Research Plan and Status. You can revise the plan later.
|
| 199 |
+
- Research Plan and Status should well organized and succinctly keep track of 1) high level plan (can be revised), 2) what steps have been done and what steps are in progress, 3) short results and conclusions of each step after it has been performed. Do not perform action in research plan.
|
| 200 |
+
- Research Plan and Status must only include progress that has been made by previous steps. It should not include results not directly confirmed by the previous observation.
|
| 201 |
+
- Follow the plan and try to achieve the goal as straightforwardly as possible. Use tools as necessary.
|
| 202 |
+
"""
|
| 203 |
+
elif (not plan) and reflect:
|
| 204 |
+
prompt_modifier = """You are a helpful biologist and expert geneticist.
|
| 205 |
+
In each round after the tool is used, conduct "reflection" step: reflect on the current state of the problem and the results of the last round. What does the observation mean? If there is an error, what caused the error and how to debug?
|
| 206 |
+
"""
|
| 207 |
+
else:
|
| 208 |
+
prompt_modifier = """You are a helpful biologist and expert geneticist.
|
| 209 |
+
Given the question from the user,
|
| 210 |
+
- First, come up with a high level plan based on your understanding of the problem and available tools and record it in the Research Plan and Status. You can revise the plan later.
|
| 211 |
+
- Research Plan and Status should well organized and succinctly keep track of 1) high level plan (can be revised), 2) what steps have been done and what steps are in progress, 3) short results and conclusions of each step after it has been performed. Do not perform action in research plan.
|
| 212 |
+
- Research Plan and Status must only include progress that has been made by previous steps. It should not include results not directly confirmed by the previous observation.
|
| 213 |
+
- Follow the plan and try to achieve the goal as straightforwardly as possible. Use tools as necessary.
|
| 214 |
+
In each round after the tool is used, conduct "reflection" step: reflect on the current state of the problem and the results of the last round. What does the observation mean? If there is an error, what caused the error and how to debug?
|
| 215 |
+
You have access to write_python_code and run_python_repl tool to write and run your own code if tools fail, or if the given tools are not enough. Please always make sure to write code when dealing with substantial data, including finding the length of long sequences or elements at different positions.
|
| 216 |
+
"""
|
| 217 |
+
|
| 218 |
+
if data_lake:
|
| 219 |
+
# Format data lake items with descriptions
|
| 220 |
+
data_lake_formatted = []
|
| 221 |
+
for item in data_lake_items:
|
| 222 |
+
description = data_lake_dict.get(item, f"Data lake item: {item}")
|
| 223 |
+
data_lake_formatted.append(f"{item}: {description}")
|
| 224 |
+
|
| 225 |
+
prompt_modifier += """
|
| 226 |
+
You can also access a biological data lake at the following path: {data_lake_path}. You can use the run_python_repl tool to write code to understand the data, process and utilize it for the task.
|
| 227 |
+
Here is the list of datasets with their descriptions:
|
| 228 |
+
----
|
| 229 |
+
{data_lake_formatted}
|
| 230 |
+
----
|
| 231 |
+
""".format(
|
| 232 |
+
data_lake_path=data_lake_path,
|
| 233 |
+
data_lake_formatted="\n".join(data_lake_formatted),
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
if library_access:
|
| 237 |
+
# Format library content with descriptions
|
| 238 |
+
library_formatted = []
|
| 239 |
+
for lib_name, lib_desc in library_content_dict.items():
|
| 240 |
+
library_formatted.append(f"{lib_name}: {lib_desc}")
|
| 241 |
+
|
| 242 |
+
prompt_modifier += """
|
| 243 |
+
You also have access to a list of software packages that can be used to perform various tasks.
|
| 244 |
+
You can use the run_python_repl tool to write code to access and utilize the library for the task.
|
| 245 |
+
Don't forget the import statement.
|
| 246 |
+
Here is the list of available libraries with their descriptions:
|
| 247 |
+
----
|
| 248 |
+
{library_formatted}
|
| 249 |
+
----
|
| 250 |
+
""".format(library_formatted="\n".join(library_formatted))
|
| 251 |
+
|
| 252 |
+
print("=" * 25 + "System Prompt" + "=" * 25)
|
| 253 |
+
print(prompt_modifier)
|
| 254 |
+
self.system_prompt = prompt_modifier
|
| 255 |
+
self.prompt = ChatPromptTemplate.from_messages(
|
| 256 |
+
[
|
| 257 |
+
("system", prompt_modifier),
|
| 258 |
+
MessagesPlaceholder(variable_name="messages"),
|
| 259 |
+
]
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
# Store the tools for later use
|
| 263 |
+
self.active_tools = tools
|
| 264 |
+
|
| 265 |
+
# Create a custom implementation of the ReAct agent using LangGraph
|
| 266 |
+
self.app = self._create_custom_react_agent(self.llm, tools, self.prompt)
|
| 267 |
+
|
| 268 |
+
def _create_custom_react_agent(self, llm, tools, prompt):
|
| 269 |
+
"""Create a custom ReAct agent using LangGraph."""
|
| 270 |
+
# Create a dictionary mapping tool names to tool objects for easy lookup
|
| 271 |
+
tools_by_name = {tool.name: tool for tool in tools}
|
| 272 |
+
|
| 273 |
+
# Bind the tools to the language model
|
| 274 |
+
llm_with_tools = llm.bind_tools(tools)
|
| 275 |
+
|
| 276 |
+
# Define the node that calls the model
|
| 277 |
+
def call_model(state: AgentState, config: RunnableConfig = None):
|
| 278 |
+
"""Node that calls the language model to get the next action."""
|
| 279 |
+
system_message = SystemMessage(content=self.system_prompt)
|
| 280 |
+
messages = [system_message] + state["messages"]
|
| 281 |
+
response = llm_with_tools.invoke(messages, config=config)
|
| 282 |
+
return {"messages": [response]}
|
| 283 |
+
|
| 284 |
+
# Define the node that executes tools
|
| 285 |
+
def tool_node(state: AgentState):
|
| 286 |
+
"""Node that executes tools based on the LLM's decisions."""
|
| 287 |
+
outputs = []
|
| 288 |
+
for tool_call in state["messages"][-1].tool_calls:
|
| 289 |
+
try:
|
| 290 |
+
tool_result = tools_by_name[tool_call["name"]].invoke(tool_call["args"])
|
| 291 |
+
outputs.append(
|
| 292 |
+
ToolMessage(
|
| 293 |
+
content=json.dumps(tool_result),
|
| 294 |
+
name=tool_call["name"],
|
| 295 |
+
tool_call_id=tool_call["id"],
|
| 296 |
+
)
|
| 297 |
+
)
|
| 298 |
+
except Exception as e:
|
| 299 |
+
# Handle any errors that occur during tool execution
|
| 300 |
+
outputs.append(
|
| 301 |
+
ToolMessage(
|
| 302 |
+
content=json.dumps({"error": str(e)}),
|
| 303 |
+
name=tool_call["name"],
|
| 304 |
+
tool_call_id=tool_call["id"],
|
| 305 |
+
)
|
| 306 |
+
)
|
| 307 |
+
return {"messages": outputs}
|
| 308 |
+
|
| 309 |
+
# Define the conditional edge that determines whether to continue or not
|
| 310 |
+
def should_continue(state: AgentState):
|
| 311 |
+
"""Determine if we should continue running the graph or finish."""
|
| 312 |
+
messages = state["messages"]
|
| 313 |
+
last_message = messages[-1]
|
| 314 |
+
# If there is no tool call, then we finish
|
| 315 |
+
if not hasattr(last_message, "tool_calls") or not last_message.tool_calls:
|
| 316 |
+
return "end"
|
| 317 |
+
# Otherwise if there is, we continue
|
| 318 |
+
else:
|
| 319 |
+
return "continue"
|
| 320 |
+
|
| 321 |
+
# Define a new graph
|
| 322 |
+
workflow = StateGraph(AgentState)
|
| 323 |
+
|
| 324 |
+
# Define the two nodes we will cycle between
|
| 325 |
+
workflow.add_node("agent", call_model)
|
| 326 |
+
workflow.add_node("tools", tool_node)
|
| 327 |
+
|
| 328 |
+
# Set the entrypoint as `agent`
|
| 329 |
+
workflow.set_entry_point("agent")
|
| 330 |
+
|
| 331 |
+
# Add conditional edges
|
| 332 |
+
workflow.add_conditional_edges(
|
| 333 |
+
"agent",
|
| 334 |
+
should_continue,
|
| 335 |
+
{
|
| 336 |
+
"continue": "tools",
|
| 337 |
+
"end": END,
|
| 338 |
+
},
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
# Add edge from tools back to agent
|
| 342 |
+
workflow.add_edge("tools", "agent")
|
| 343 |
+
|
| 344 |
+
# Compile the graph
|
| 345 |
+
return workflow.compile()
|
| 346 |
+
|
| 347 |
+
def go(self, prompt):
|
| 348 |
+
"""Execute the agent with the given prompt.
|
| 349 |
+
|
| 350 |
+
Args:
|
| 351 |
+
prompt: The user's query
|
| 352 |
+
|
| 353 |
+
"""
|
| 354 |
+
if self.use_tool_retriever:
|
| 355 |
+
# Gather all available tools from the registry
|
| 356 |
+
all_tools = self.tool_registry.tools if hasattr(self, "tool_registry") else []
|
| 357 |
+
|
| 358 |
+
# Get data lake items with descriptions
|
| 359 |
+
data_lake_path = self.path + "/data_lake"
|
| 360 |
+
data_lake_content = glob.glob(data_lake_path + "/*")
|
| 361 |
+
data_lake_items = [x.split("/")[-1] for x in data_lake_content]
|
| 362 |
+
|
| 363 |
+
# Create data lake descriptions for retrieval
|
| 364 |
+
data_lake_descriptions = []
|
| 365 |
+
for item in data_lake_items:
|
| 366 |
+
description = self.data_lake_dict.get(item, f"Data lake item: {item}")
|
| 367 |
+
data_lake_descriptions.append({"name": item, "description": description})
|
| 368 |
+
|
| 369 |
+
# Libraries with descriptions
|
| 370 |
+
library_descriptions = []
|
| 371 |
+
for lib_name, lib_desc in self.library_content_dict.items():
|
| 372 |
+
library_descriptions.append({"name": lib_name, "description": lib_desc})
|
| 373 |
+
|
| 374 |
+
# Prepare resources for retrieval
|
| 375 |
+
resources = {
|
| 376 |
+
"tools": all_tools,
|
| 377 |
+
"data_lake": data_lake_descriptions,
|
| 378 |
+
"libraries": library_descriptions,
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
# Use prompt-based retrieval with the agent's LLM
|
| 382 |
+
selected_resources = self.retriever.prompt_based_retrieval(prompt, resources, llm=self.llm)
|
| 383 |
+
print("Using prompt-based retrieval with the agent's LLM")
|
| 384 |
+
|
| 385 |
+
# If we're using prompt or embedding based retrieval, print the selected resources
|
| 386 |
+
print("\nSelected tools:")
|
| 387 |
+
for tool in selected_resources["tools"]:
|
| 388 |
+
if isinstance(tool, dict):
|
| 389 |
+
print(f"- {tool.get('name', 'Unknown')}: {tool.get('description', '')}")
|
| 390 |
+
else:
|
| 391 |
+
print(f"- {getattr(tool, 'name', str(tool))}: {getattr(tool, 'description', '')}")
|
| 392 |
+
|
| 393 |
+
print("\nSelected data lake items:")
|
| 394 |
+
for item in selected_resources["data_lake"]:
|
| 395 |
+
if isinstance(item, dict):
|
| 396 |
+
name = item.get("name", "Unknown")
|
| 397 |
+
description = self.data_lake_dict.get(name, f"Data lake item: {name}")
|
| 398 |
+
print(f"- {name}: {description}")
|
| 399 |
+
elif isinstance(item, str) and ": " in item:
|
| 400 |
+
# If the item already has a description, print it as is
|
| 401 |
+
print(f"- {item}")
|
| 402 |
+
else:
|
| 403 |
+
description = self.data_lake_dict.get(item, f"Data lake item: {item}")
|
| 404 |
+
print(f"- {item}: {description}")
|
| 405 |
+
|
| 406 |
+
print("\nSelected libraries:")
|
| 407 |
+
for lib in selected_resources["libraries"]:
|
| 408 |
+
if isinstance(lib, dict):
|
| 409 |
+
print(f"- {lib.get('name', 'Unknown')}: {lib.get('description', '')}")
|
| 410 |
+
else:
|
| 411 |
+
print(f"- {lib}")
|
| 412 |
+
|
| 413 |
+
# Convert selected tools to langchain tool objects
|
| 414 |
+
tool_names = [
|
| 415 |
+
tool["name"] if isinstance(tool, dict) else getattr(tool, "name", str(tool))
|
| 416 |
+
for tool in selected_resources["tools"]
|
| 417 |
+
]
|
| 418 |
+
retrieved_list_of_tools = []
|
| 419 |
+
|
| 420 |
+
# Get the tool objects by name
|
| 421 |
+
for tool_name in tool_names:
|
| 422 |
+
# Find the tool in the original tools list
|
| 423 |
+
matching_tools = [t for t in self.tools if getattr(t, "name", None) == tool_name]
|
| 424 |
+
if matching_tools:
|
| 425 |
+
retrieved_list_of_tools.append(matching_tools[0])
|
| 426 |
+
|
| 427 |
+
# Add back coding tool if not already included
|
| 428 |
+
if len([i for i in retrieved_list_of_tools if i.name == "run_python_repl"]) == 0:
|
| 429 |
+
retrieved_list_of_tools = retrieved_list_of_tools + [
|
| 430 |
+
i for i in self.tools if i.name == "run_python_repl"
|
| 431 |
+
]
|
| 432 |
+
|
| 433 |
+
print("Retrieved tools: \n" + "\n".join([l.name + ": " + l.description for l in retrieved_list_of_tools]))
|
| 434 |
+
# Recreate the custom agent with the retrieved tools
|
| 435 |
+
self.app = self._create_custom_react_agent(self.llm, retrieved_list_of_tools, self.prompt)
|
| 436 |
+
|
| 437 |
+
# Default behavior (no tool retriever or retrieval_method is 'none')
|
| 438 |
+
config = {"recursion_limit": 50}
|
| 439 |
+
inputs = {"messages": [("user", prompt)]}
|
| 440 |
+
self.log = []
|
| 441 |
+
for s in self.app.stream(inputs, stream_mode="values", config=config):
|
| 442 |
+
message = s["messages"][-1]
|
| 443 |
+
out = pretty_print(message)
|
| 444 |
+
self.log.append(out)
|
| 445 |
+
return self.log, s["messages"][-1].content
|
| 446 |
+
|
| 447 |
+
def result_formatting(self, output_class, task_intention):
|
| 448 |
+
self.format_check_prompt = ChatPromptTemplate.from_messages(
|
| 449 |
+
[
|
| 450 |
+
(
|
| 451 |
+
"system",
|
| 452 |
+
(
|
| 453 |
+
"You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
|
| 454 |
+
"Review the entire history of messages provided. "
|
| 455 |
+
"Here is the task output requirement: \n"
|
| 456 |
+
f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
|
| 457 |
+
),
|
| 458 |
+
),
|
| 459 |
+
("placeholder", "{messages}"),
|
| 460 |
+
]
|
| 461 |
+
)
|
| 462 |
+
|
| 463 |
+
checker_llm = self.format_check_prompt | self.llm.with_structured_output(output_class)
|
| 464 |
+
result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
|
| 465 |
+
return result
|
Biomanus/biomni/biorxiv_scripts/extract_biorxiv_tasks.py
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Script to extract common tasks, databases, and software from bioRxiv papers
|
| 3 |
+
using the PaperTaskExtractor and existing metadata CSV.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
import io
|
| 8 |
+
import json
|
| 9 |
+
import os
|
| 10 |
+
import random
|
| 11 |
+
import sys
|
| 12 |
+
import time
|
| 13 |
+
from typing import Any
|
| 14 |
+
|
| 15 |
+
import pandas as pd
|
| 16 |
+
import PyPDF2
|
| 17 |
+
import requests
|
| 18 |
+
from tqdm import tqdm
|
| 19 |
+
|
| 20 |
+
# Add the parent directory to the path so we can import the bioagentos package
|
| 21 |
+
sys.path.append("../../")
|
| 22 |
+
from biomni.agent.env_collection import PaperTaskExtractor
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def parse_arguments():
|
| 26 |
+
"""Parse command line arguments."""
|
| 27 |
+
parser = argparse.ArgumentParser(description="Extract tasks from bioRxiv papers in a specific subject area.")
|
| 28 |
+
parser.add_argument(
|
| 29 |
+
"--subject",
|
| 30 |
+
type=str,
|
| 31 |
+
required=True,
|
| 32 |
+
help='Subject area to search for papers (e.g., "neuroscience", "bioinformatics")',
|
| 33 |
+
)
|
| 34 |
+
parser.add_argument(
|
| 35 |
+
"--limit",
|
| 36 |
+
type=int,
|
| 37 |
+
default=10,
|
| 38 |
+
help="Maximum number of papers to process (default: 10)",
|
| 39 |
+
)
|
| 40 |
+
parser.add_argument(
|
| 41 |
+
"--metadata-path",
|
| 42 |
+
type=str,
|
| 43 |
+
default="/dfs/user/kexinh/BioAgentOS/data/biorxiv_metadata.csv",
|
| 44 |
+
help="Path to bioRxiv metadata CSV file (default: data/biorxiv_metadata.csv)",
|
| 45 |
+
)
|
| 46 |
+
parser.add_argument(
|
| 47 |
+
"--output-dir",
|
| 48 |
+
type=str,
|
| 49 |
+
default=None,
|
| 50 |
+
help="Directory to save results (default: ./biorxiv_results_{subject}_{limit})",
|
| 51 |
+
)
|
| 52 |
+
parser.add_argument(
|
| 53 |
+
"--model",
|
| 54 |
+
type=str,
|
| 55 |
+
default="claude-3-haiku-20240307",
|
| 56 |
+
help="LLM model to use for extraction (default: claude-3-haiku-20240307)",
|
| 57 |
+
)
|
| 58 |
+
parser.add_argument(
|
| 59 |
+
"--chunk-size",
|
| 60 |
+
type=int,
|
| 61 |
+
default=4000,
|
| 62 |
+
help="Chunk size for text processing (default: 4000)",
|
| 63 |
+
)
|
| 64 |
+
parser.add_argument(
|
| 65 |
+
"--chunk-overlap",
|
| 66 |
+
type=int,
|
| 67 |
+
default=400,
|
| 68 |
+
help="Chunk overlap for text processing (default: 400)",
|
| 69 |
+
)
|
| 70 |
+
parser.add_argument(
|
| 71 |
+
"--max-paper-length",
|
| 72 |
+
type=int,
|
| 73 |
+
default=200000,
|
| 74 |
+
help="Maximum paper length in characters (default: 200000)",
|
| 75 |
+
)
|
| 76 |
+
parser.add_argument("--save-pdfs", action="store_true", help="Save downloaded PDFs (default: False)")
|
| 77 |
+
parser.add_argument(
|
| 78 |
+
"--random-sample",
|
| 79 |
+
action="store_true",
|
| 80 |
+
help="Randomly sample papers instead of taking the first N (default: False)",
|
| 81 |
+
)
|
| 82 |
+
return parser.parse_args()
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def load_papers_from_csv(metadata_path: str, subject: str, limit: int, random_sample: bool) -> pd.DataFrame:
|
| 86 |
+
"""Load papers from bioRxiv metadata CSV file.
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
metadata_path: Path to the metadata CSV file
|
| 90 |
+
subject: Subject area to filter by
|
| 91 |
+
limit: Maximum number of papers to return
|
| 92 |
+
random_sample: Whether to randomly sample papers
|
| 93 |
+
|
| 94 |
+
Returns:
|
| 95 |
+
DataFrame of filtered papers
|
| 96 |
+
|
| 97 |
+
"""
|
| 98 |
+
try:
|
| 99 |
+
# Load the metadata CSV
|
| 100 |
+
df_biorxiv = pd.read_csv(metadata_path)
|
| 101 |
+
|
| 102 |
+
# Filter published papers in the specified subject
|
| 103 |
+
if subject.lower() == "all":
|
| 104 |
+
filtered_df = df_biorxiv[df_biorxiv.published != "NA"]
|
| 105 |
+
else:
|
| 106 |
+
filtered_df = df_biorxiv[
|
| 107 |
+
(df_biorxiv.published != "NA") & (df_biorxiv.category.str.lower() == subject.lower())
|
| 108 |
+
]
|
| 109 |
+
|
| 110 |
+
# Sample papers
|
| 111 |
+
if len(filtered_df) > limit:
|
| 112 |
+
filtered_df = filtered_df.sample(limit, random_state=42) if random_sample else filtered_df.head(limit)
|
| 113 |
+
|
| 114 |
+
print(f"Loaded {len(filtered_df)} papers in subject: {subject}")
|
| 115 |
+
return filtered_df
|
| 116 |
+
|
| 117 |
+
except Exception as e:
|
| 118 |
+
print(f"Error loading papers from CSV: {e}")
|
| 119 |
+
return pd.DataFrame()
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def download_pdf(paper_doi: str, save_path: str | None = None) -> str | None:
|
| 123 |
+
"""Download PDF for a given paper DOI.
|
| 124 |
+
|
| 125 |
+
Args:
|
| 126 |
+
paper_doi: DOI of the paper
|
| 127 |
+
save_path: Path to save the PDF (if None, won't save to disk)
|
| 128 |
+
|
| 129 |
+
Returns:
|
| 130 |
+
PDF text content or None if download failed
|
| 131 |
+
|
| 132 |
+
"""
|
| 133 |
+
# Construct PDF URL from DOI
|
| 134 |
+
pdf_url = f"https://www.biorxiv.org/content/{paper_doi}v1.full.pdf"
|
| 135 |
+
|
| 136 |
+
try:
|
| 137 |
+
# Add random delay to avoid rate limiting
|
| 138 |
+
time.sleep(random.uniform(1, 3))
|
| 139 |
+
|
| 140 |
+
response = requests.get(pdf_url, stream=True)
|
| 141 |
+
response.raise_for_status()
|
| 142 |
+
|
| 143 |
+
# Save PDF if requested
|
| 144 |
+
if save_path:
|
| 145 |
+
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
| 146 |
+
with open(save_path, "wb") as f:
|
| 147 |
+
for chunk in response.iter_content(chunk_size=8192):
|
| 148 |
+
f.write(chunk)
|
| 149 |
+
|
| 150 |
+
# Extract text from PDF
|
| 151 |
+
pdf_text = extract_text_from_pdf(io.BytesIO(response.content))
|
| 152 |
+
return pdf_text
|
| 153 |
+
|
| 154 |
+
except requests.exceptions.RequestException as e:
|
| 155 |
+
print(f"Error downloading PDF for {paper_doi}: {e}")
|
| 156 |
+
return None
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def extract_text_from_pdf(pdf_file) -> str:
|
| 160 |
+
"""Extract text content from a PDF file.
|
| 161 |
+
|
| 162 |
+
Args:
|
| 163 |
+
pdf_file: File-like object containing PDF data
|
| 164 |
+
|
| 165 |
+
Returns:
|
| 166 |
+
Extracted text content
|
| 167 |
+
|
| 168 |
+
"""
|
| 169 |
+
try:
|
| 170 |
+
pdf_reader = PyPDF2.PdfReader(pdf_file)
|
| 171 |
+
text = ""
|
| 172 |
+
for page_num in range(len(pdf_reader.pages)):
|
| 173 |
+
page = pdf_reader.pages[page_num]
|
| 174 |
+
text += page.extract_text() + "\n\n"
|
| 175 |
+
return text
|
| 176 |
+
except Exception as e:
|
| 177 |
+
print(f"Error extracting text from PDF: {e}")
|
| 178 |
+
return ""
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def truncate_text(text: str, max_length: int) -> str:
|
| 182 |
+
"""Truncate text to a maximum length while preserving complete sentences.
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
text: The text to truncate
|
| 186 |
+
max_length: Maximum length in characters
|
| 187 |
+
|
| 188 |
+
Returns:
|
| 189 |
+
Truncated text
|
| 190 |
+
|
| 191 |
+
"""
|
| 192 |
+
if len(text) <= max_length:
|
| 193 |
+
return text
|
| 194 |
+
|
| 195 |
+
# Find the last sentence boundary before max_length
|
| 196 |
+
truncated = text[:max_length]
|
| 197 |
+
last_period = truncated.rfind(".")
|
| 198 |
+
last_question = truncated.rfind("?")
|
| 199 |
+
last_exclamation = truncated.rfind("!")
|
| 200 |
+
|
| 201 |
+
# Find the last sentence boundary
|
| 202 |
+
last_boundary = max(last_period, last_question, last_exclamation)
|
| 203 |
+
|
| 204 |
+
if last_boundary > 0:
|
| 205 |
+
return text[: last_boundary + 1]
|
| 206 |
+
else:
|
| 207 |
+
# If no sentence boundary found, just truncate at max_length
|
| 208 |
+
return truncated
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def process_papers(papers_df: pd.DataFrame, args) -> list[dict[str, Any]]:
|
| 212 |
+
"""Process papers using PaperTaskExtractor.
|
| 213 |
+
|
| 214 |
+
Args:
|
| 215 |
+
papers_df: DataFrame of paper metadata
|
| 216 |
+
args: Command line arguments
|
| 217 |
+
|
| 218 |
+
Returns:
|
| 219 |
+
List of papers with extracted tasks, databases, and software
|
| 220 |
+
|
| 221 |
+
"""
|
| 222 |
+
# Initialize the task extractor
|
| 223 |
+
extractor = PaperTaskExtractor(llm=args.model, chunk_size=args.chunk_size, chunk_overlap=args.chunk_overlap)
|
| 224 |
+
|
| 225 |
+
results = []
|
| 226 |
+
|
| 227 |
+
for _, paper in tqdm(papers_df.iterrows(), total=len(papers_df), desc="Processing papers"):
|
| 228 |
+
paper_doi = paper.get("doi")
|
| 229 |
+
if not paper_doi:
|
| 230 |
+
continue
|
| 231 |
+
|
| 232 |
+
print(f"\nProcessing paper: {paper.get('title')} (DOI: {paper_doi})")
|
| 233 |
+
|
| 234 |
+
# Create paths
|
| 235 |
+
pdf_path = None
|
| 236 |
+
if args.save_pdfs:
|
| 237 |
+
pdf_path = os.path.join(args.output_dir, "pdfs", f"{paper_doi.replace('/', '_')}.pdf")
|
| 238 |
+
|
| 239 |
+
result_path = os.path.join(args.output_dir, "results", f"{paper_doi.replace('/', '_')}.json")
|
| 240 |
+
|
| 241 |
+
# Skip if already processed
|
| 242 |
+
if os.path.exists(result_path):
|
| 243 |
+
print(f"Paper already processed, loading from {result_path}")
|
| 244 |
+
with open(result_path) as f:
|
| 245 |
+
paper_result = json.load(f)
|
| 246 |
+
results.append(paper_result)
|
| 247 |
+
continue
|
| 248 |
+
|
| 249 |
+
# Download and process PDF
|
| 250 |
+
pdf_text = download_pdf(paper_doi, pdf_path)
|
| 251 |
+
if not pdf_text:
|
| 252 |
+
print(f"Failed to download or extract text from {paper_doi}")
|
| 253 |
+
continue
|
| 254 |
+
|
| 255 |
+
# Truncate text if it exceeds max length
|
| 256 |
+
if len(pdf_text) > args.max_paper_length:
|
| 257 |
+
original_length = len(pdf_text)
|
| 258 |
+
pdf_text = truncate_text(pdf_text, args.max_paper_length)
|
| 259 |
+
print(f"Truncated paper from {original_length} to {len(pdf_text)} characters")
|
| 260 |
+
|
| 261 |
+
# Extract tasks, databases, and software
|
| 262 |
+
try:
|
| 263 |
+
_, extraction_results = extractor.go(pdf_text)
|
| 264 |
+
|
| 265 |
+
# Add paper metadata to results
|
| 266 |
+
paper_result = {
|
| 267 |
+
"metadata": {
|
| 268 |
+
"doi": paper.get("doi"),
|
| 269 |
+
"title": paper.get("title"),
|
| 270 |
+
"authors": paper.get("authors", ""),
|
| 271 |
+
"abstract": paper.get("abstract", ""),
|
| 272 |
+
"date": paper.get("date", ""),
|
| 273 |
+
"category": paper.get("category", ""),
|
| 274 |
+
"text_length": len(pdf_text),
|
| 275 |
+
},
|
| 276 |
+
"extraction": extraction_results,
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
# Save results
|
| 280 |
+
os.makedirs(os.path.dirname(result_path), exist_ok=True)
|
| 281 |
+
with open(result_path, "w") as f:
|
| 282 |
+
json.dump(paper_result, f, indent=2)
|
| 283 |
+
|
| 284 |
+
results.append(paper_result)
|
| 285 |
+
|
| 286 |
+
except Exception as e:
|
| 287 |
+
print(f"Error processing paper {paper_doi}: {e}")
|
| 288 |
+
|
| 289 |
+
return results
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def generate_summary(results: list[dict[str, Any]], output_dir: str):
|
| 293 |
+
"""Generate summary of extracted tasks, databases, and software.
|
| 294 |
+
|
| 295 |
+
Args:
|
| 296 |
+
results: List of papers with extraction results
|
| 297 |
+
output_dir: Directory to save summary
|
| 298 |
+
|
| 299 |
+
"""
|
| 300 |
+
# Collect all tasks, databases, and software
|
| 301 |
+
all_tasks = []
|
| 302 |
+
all_databases = []
|
| 303 |
+
all_software = []
|
| 304 |
+
|
| 305 |
+
for paper in results:
|
| 306 |
+
extraction = paper.get("extraction", {})
|
| 307 |
+
|
| 308 |
+
# Add paper metadata to each item
|
| 309 |
+
paper_meta = paper.get("metadata", {})
|
| 310 |
+
paper_id = f"{paper_meta.get('title')} ({paper_meta.get('doi')})"
|
| 311 |
+
|
| 312 |
+
# Process tasks
|
| 313 |
+
for task in extraction.get("tasks", []):
|
| 314 |
+
task["paper"] = paper_id
|
| 315 |
+
all_tasks.append(task)
|
| 316 |
+
|
| 317 |
+
# Process databases
|
| 318 |
+
for db in extraction.get("databases", []):
|
| 319 |
+
db["paper"] = paper_id
|
| 320 |
+
all_databases.append(db)
|
| 321 |
+
|
| 322 |
+
# Process software
|
| 323 |
+
for sw in extraction.get("software", []):
|
| 324 |
+
sw["paper"] = paper_id
|
| 325 |
+
all_software.append(sw)
|
| 326 |
+
|
| 327 |
+
# Create summary dataframes
|
| 328 |
+
if all_tasks:
|
| 329 |
+
tasks_df = pd.DataFrame(all_tasks)
|
| 330 |
+
tasks_df.to_csv(os.path.join(output_dir, "tasks_summary.csv"), index=False)
|
| 331 |
+
|
| 332 |
+
if all_databases:
|
| 333 |
+
db_df = pd.DataFrame(all_databases)
|
| 334 |
+
db_df.to_csv(os.path.join(output_dir, "databases_summary.csv"), index=False)
|
| 335 |
+
|
| 336 |
+
if all_software:
|
| 337 |
+
sw_df = pd.DataFrame(all_software)
|
| 338 |
+
sw_df.to_csv(os.path.join(output_dir, "software_summary.csv"), index=False)
|
| 339 |
+
|
| 340 |
+
# Generate frequency counts
|
| 341 |
+
task_counts = {}
|
| 342 |
+
for task in all_tasks:
|
| 343 |
+
task_name = task.get("task_name")
|
| 344 |
+
if task_name:
|
| 345 |
+
task_counts[task_name] = task_counts.get(task_name, 0) + 1
|
| 346 |
+
|
| 347 |
+
db_counts = {}
|
| 348 |
+
for db in all_databases:
|
| 349 |
+
db_name = db.get("name")
|
| 350 |
+
if db_name:
|
| 351 |
+
db_counts[db_name] = db_counts.get(db_name, 0) + 1
|
| 352 |
+
|
| 353 |
+
sw_counts = {}
|
| 354 |
+
for sw in all_software:
|
| 355 |
+
sw_name = sw.get("name")
|
| 356 |
+
if sw_name:
|
| 357 |
+
sw_counts[sw_name] = sw_counts.get(sw_name, 0) + 1
|
| 358 |
+
|
| 359 |
+
# Save frequency counts
|
| 360 |
+
with open(os.path.join(output_dir, "frequency_summary.json"), "w") as f:
|
| 361 |
+
json.dump(
|
| 362 |
+
{"tasks": task_counts, "databases": db_counts, "software": sw_counts},
|
| 363 |
+
f,
|
| 364 |
+
indent=2,
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
print(f"Summary files saved to {output_dir}")
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
def main():
|
| 371 |
+
"""Main function to run the script."""
|
| 372 |
+
args = parse_arguments()
|
| 373 |
+
|
| 374 |
+
# Set default output directory if not specified
|
| 375 |
+
if args.output_dir is None:
|
| 376 |
+
# Clean subject name for directory name (replace spaces with underscores, lowercase)
|
| 377 |
+
clean_subject = args.subject.lower().replace(" ", "_").replace("/", "_")
|
| 378 |
+
args.output_dir = f"./biorxiv_results_{clean_subject}_{args.limit}"
|
| 379 |
+
|
| 380 |
+
# Create output directories
|
| 381 |
+
os.makedirs(args.output_dir, exist_ok=True)
|
| 382 |
+
os.makedirs(os.path.join(args.output_dir, "results"), exist_ok=True)
|
| 383 |
+
if args.save_pdfs:
|
| 384 |
+
os.makedirs(os.path.join(args.output_dir, "pdfs"), exist_ok=True)
|
| 385 |
+
|
| 386 |
+
# Load papers from CSV
|
| 387 |
+
print(f"Loading papers from {args.metadata_path} in subject area: {args.subject}")
|
| 388 |
+
papers_df = load_papers_from_csv(args.metadata_path, args.subject, args.limit, args.random_sample)
|
| 389 |
+
|
| 390 |
+
if len(papers_df) == 0:
|
| 391 |
+
print(f"No papers found for subject: {args.subject}")
|
| 392 |
+
return
|
| 393 |
+
|
| 394 |
+
# Process papers
|
| 395 |
+
results = process_papers(papers_df, args)
|
| 396 |
+
|
| 397 |
+
# Generate summary
|
| 398 |
+
generate_summary(results, args.output_dir)
|
| 399 |
+
|
| 400 |
+
print("Done!")
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
if __name__ == "__main__":
|
| 404 |
+
main()
|
Biomanus/biomni/biorxiv_scripts/generate_function.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Command-line tool to generate Python functions from task descriptions using the function_generator agent."""
|
| 3 |
+
|
| 4 |
+
import argparse
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
from biomni.agent.function_generator import FunctionGenerator
|
| 9 |
+
from tqdm import tqdm
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def main():
|
| 13 |
+
"""Main function for the command-line tool."""
|
| 14 |
+
parser = argparse.ArgumentParser(description="Generate Python functions given task descriptions")
|
| 15 |
+
parser.add_argument(
|
| 16 |
+
"--task",
|
| 17 |
+
"-t",
|
| 18 |
+
type=str,
|
| 19 |
+
help="JSON file containing a list of task descriptions",
|
| 20 |
+
)
|
| 21 |
+
parser.add_argument(
|
| 22 |
+
"--output-dir",
|
| 23 |
+
"-o",
|
| 24 |
+
type=str,
|
| 25 |
+
default="generated_functions",
|
| 26 |
+
help="Directory to save the generated functions (default: generated_functions)",
|
| 27 |
+
)
|
| 28 |
+
parser.add_argument(
|
| 29 |
+
"--model",
|
| 30 |
+
"-m",
|
| 31 |
+
type=str,
|
| 32 |
+
default="claude-3-7-sonnet-latest",
|
| 33 |
+
help="LLM model to use (default: claude-3-7-sonnet-latest)",
|
| 34 |
+
)
|
| 35 |
+
parser.add_argument(
|
| 36 |
+
"--temperature",
|
| 37 |
+
type=float,
|
| 38 |
+
default=0.7,
|
| 39 |
+
help="Temperature setting for the LLM (default: 0.7)",
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
args = parser.parse_args()
|
| 43 |
+
|
| 44 |
+
with open(args.task) as f_tasks:
|
| 45 |
+
task_list = json.load(f_tasks)
|
| 46 |
+
task_descriptions = list(task_list["tasks"])
|
| 47 |
+
|
| 48 |
+
# Create the output directory if it doesn't exist
|
| 49 |
+
os.makedirs(args.output_dir, exist_ok=True)
|
| 50 |
+
|
| 51 |
+
# Initialize the function generator agent
|
| 52 |
+
function_generator = FunctionGenerator(llm=args.model, temperature=args.temperature)
|
| 53 |
+
|
| 54 |
+
if not task_list:
|
| 55 |
+
print("No tasks found.")
|
| 56 |
+
else:
|
| 57 |
+
# tqdm shows a progress bar, file names as description
|
| 58 |
+
for _i, desc in enumerate(tqdm(task_descriptions, desc="Generating Python scripts given task descriptions"), 1):
|
| 59 |
+
generated_script_name, generated_codes = function_generator.go(desc)
|
| 60 |
+
|
| 61 |
+
# Save results
|
| 62 |
+
result_path = os.path.join(args.output_dir, generated_script_name)
|
| 63 |
+
|
| 64 |
+
os.makedirs(os.path.dirname(result_path), exist_ok=True)
|
| 65 |
+
with open(result_path, "w") as f:
|
| 66 |
+
f.write(generated_codes)
|
| 67 |
+
|
| 68 |
+
print("DONE")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
if __name__ == "__main__":
|
| 72 |
+
main()
|
Biomanus/biomni/biorxiv_scripts/process_all_subjects.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Script to process all bioRxiv subjects and extract computational tasks from the top 100 papers in each."""
|
| 3 |
+
|
| 4 |
+
import argparse
|
| 5 |
+
import os
|
| 6 |
+
import subprocess
|
| 7 |
+
import sys
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
import pandas as pd
|
| 11 |
+
from tqdm import tqdm
|
| 12 |
+
|
| 13 |
+
# Add the parent directory to the path so we can import the bioagentos package
|
| 14 |
+
sys.path.append("/dfs/user/kexinh/BioAgentOS/")
|
| 15 |
+
|
| 16 |
+
# List of all bioRxiv subjects
|
| 17 |
+
BIORXIV_SUBJECTS = [
|
| 18 |
+
"evolutionary biology",
|
| 19 |
+
"ecology",
|
| 20 |
+
"neuroscience",
|
| 21 |
+
"developmental biology",
|
| 22 |
+
"plant biology",
|
| 23 |
+
"microbiology",
|
| 24 |
+
"cancer biology",
|
| 25 |
+
"immunology",
|
| 26 |
+
"cell biology",
|
| 27 |
+
"biochemistry",
|
| 28 |
+
"genetics",
|
| 29 |
+
"bioinformatics",
|
| 30 |
+
"animal behavior and cognition",
|
| 31 |
+
"biophysics",
|
| 32 |
+
"genomics",
|
| 33 |
+
"systems biology",
|
| 34 |
+
"bioengineering",
|
| 35 |
+
"molecular biology",
|
| 36 |
+
"physiology",
|
| 37 |
+
"zoology",
|
| 38 |
+
"scientific communication and education",
|
| 39 |
+
"pathology",
|
| 40 |
+
"synthetic biology",
|
| 41 |
+
"paleontology",
|
| 42 |
+
"pharmacology and toxicology",
|
| 43 |
+
]
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def parse_arguments():
|
| 47 |
+
"""Parse command line arguments."""
|
| 48 |
+
parser = argparse.ArgumentParser(description="Process all bioRxiv subjects and extract tasks from papers.")
|
| 49 |
+
parser.add_argument(
|
| 50 |
+
"--papers-per-subject",
|
| 51 |
+
type=int,
|
| 52 |
+
default=100,
|
| 53 |
+
help="Number of papers to process per subject (default: 100)",
|
| 54 |
+
)
|
| 55 |
+
parser.add_argument(
|
| 56 |
+
"--metadata-path",
|
| 57 |
+
type=str,
|
| 58 |
+
default="/dfs/user/kexinh/BioAgentOS/data/biorxiv_metadata.csv",
|
| 59 |
+
help="Path to bioRxiv metadata CSV file (default: data/biorxiv_metadata.csv)",
|
| 60 |
+
)
|
| 61 |
+
parser.add_argument(
|
| 62 |
+
"--output-dir",
|
| 63 |
+
type=str,
|
| 64 |
+
default="./biorxiv_all_subjects",
|
| 65 |
+
help="Base directory to save results (default: ./biorxiv_all_subjects)",
|
| 66 |
+
)
|
| 67 |
+
parser.add_argument(
|
| 68 |
+
"--model",
|
| 69 |
+
type=str,
|
| 70 |
+
default="claude-3-haiku-20240307",
|
| 71 |
+
help="LLM model to use for extraction (default: claude-3-haiku-20240307)",
|
| 72 |
+
)
|
| 73 |
+
parser.add_argument(
|
| 74 |
+
"--max-paper-length",
|
| 75 |
+
type=int,
|
| 76 |
+
default=200000,
|
| 77 |
+
help="Maximum paper length in characters (default: 200000)",
|
| 78 |
+
)
|
| 79 |
+
parser.add_argument(
|
| 80 |
+
"--chunk-size",
|
| 81 |
+
type=int,
|
| 82 |
+
default=10000,
|
| 83 |
+
help="Chunk size for text processing (default: 10000)",
|
| 84 |
+
)
|
| 85 |
+
parser.add_argument(
|
| 86 |
+
"--chunk-overlap",
|
| 87 |
+
type=int,
|
| 88 |
+
default=400,
|
| 89 |
+
help="Chunk overlap for text processing (default: 400)",
|
| 90 |
+
)
|
| 91 |
+
parser.add_argument("--save-pdfs", action="store_true", help="Save downloaded PDFs (default: False)")
|
| 92 |
+
parser.add_argument(
|
| 93 |
+
"--random-sample",
|
| 94 |
+
action="store_true",
|
| 95 |
+
help="Randomly sample papers instead of taking the first N (default: False)",
|
| 96 |
+
)
|
| 97 |
+
parser.add_argument(
|
| 98 |
+
"--subjects",
|
| 99 |
+
type=str,
|
| 100 |
+
nargs="+",
|
| 101 |
+
help="Specific subjects to process (default: all subjects)",
|
| 102 |
+
)
|
| 103 |
+
parser.add_argument(
|
| 104 |
+
"--delay",
|
| 105 |
+
type=int,
|
| 106 |
+
default=60,
|
| 107 |
+
help="Delay in seconds between processing subjects (default: 60)",
|
| 108 |
+
)
|
| 109 |
+
parser.add_argument(
|
| 110 |
+
"--summary-only",
|
| 111 |
+
action="store_true",
|
| 112 |
+
help="Only generate the combined summary (default: False)",
|
| 113 |
+
)
|
| 114 |
+
return parser.parse_args()
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def check_subject_availability(metadata_path, subjects):
|
| 118 |
+
"""Check which subjects have papers available in the metadata.
|
| 119 |
+
|
| 120 |
+
Args:
|
| 121 |
+
metadata_path: Path to the metadata CSV file
|
| 122 |
+
subjects: List of subjects to check
|
| 123 |
+
|
| 124 |
+
Returns:
|
| 125 |
+
List of subjects with available papers
|
| 126 |
+
|
| 127 |
+
"""
|
| 128 |
+
try:
|
| 129 |
+
df_biorxiv = pd.read_csv(metadata_path)
|
| 130 |
+
available_subjects = []
|
| 131 |
+
|
| 132 |
+
for subject in subjects:
|
| 133 |
+
count = len(
|
| 134 |
+
df_biorxiv[(df_biorxiv.published != "NA") & (df_biorxiv.category.str.lower() == subject.lower())]
|
| 135 |
+
)
|
| 136 |
+
if count > 0:
|
| 137 |
+
available_subjects.append((subject, count))
|
| 138 |
+
print(f"Subject '{subject}' has {count} papers available")
|
| 139 |
+
else:
|
| 140 |
+
print(f"Subject '{subject}' has no papers available")
|
| 141 |
+
|
| 142 |
+
return available_subjects
|
| 143 |
+
except Exception as e:
|
| 144 |
+
print(f"Error checking subject availability: {e}")
|
| 145 |
+
return []
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def process_subject(subject, args):
|
| 149 |
+
"""Process a single subject by calling the extract_biorxiv_tasks.py script.
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
subject: Subject to process
|
| 153 |
+
args: Command line arguments
|
| 154 |
+
|
| 155 |
+
"""
|
| 156 |
+
# Clean subject name for command line
|
| 157 |
+
subject.replace(" ", "\\ ")
|
| 158 |
+
|
| 159 |
+
# Build command
|
| 160 |
+
cmd = [
|
| 161 |
+
"python",
|
| 162 |
+
"BioAgentOS/bioagentos/scripts/extract_biorxiv_tasks.py",
|
| 163 |
+
"--subject",
|
| 164 |
+
f'"{subject}"',
|
| 165 |
+
"--limit",
|
| 166 |
+
str(args.papers_per_subject),
|
| 167 |
+
"--metadata-path",
|
| 168 |
+
args.metadata_path,
|
| 169 |
+
"--model",
|
| 170 |
+
args.model,
|
| 171 |
+
"--max-paper-length",
|
| 172 |
+
str(args.max_paper_length),
|
| 173 |
+
"--chunk-size",
|
| 174 |
+
str(args.chunk_size),
|
| 175 |
+
"--chunk-overlap",
|
| 176 |
+
str(args.chunk_overlap),
|
| 177 |
+
]
|
| 178 |
+
|
| 179 |
+
# Add optional arguments
|
| 180 |
+
if args.save_pdfs:
|
| 181 |
+
cmd.append("--save-pdfs")
|
| 182 |
+
if args.random_sample:
|
| 183 |
+
cmd.append("--random-sample")
|
| 184 |
+
|
| 185 |
+
# Convert command list to string
|
| 186 |
+
cmd_str = " ".join(cmd)
|
| 187 |
+
|
| 188 |
+
print(f"\n{'=' * 80}\nProcessing subject: {subject}\n{'=' * 80}")
|
| 189 |
+
print(f"Running command: {cmd_str}")
|
| 190 |
+
|
| 191 |
+
# Execute command
|
| 192 |
+
try:
|
| 193 |
+
subprocess.run(cmd_str, shell=True, check=True)
|
| 194 |
+
print(f"Successfully processed subject: {subject}")
|
| 195 |
+
except subprocess.CalledProcessError as e:
|
| 196 |
+
print(f"Error processing subject '{subject}': {e}")
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def combine_summaries(base_dir, subjects, papers_per_subject):
|
| 200 |
+
"""Combine frequency summaries from all subjects into a single summary.
|
| 201 |
+
|
| 202 |
+
Args:
|
| 203 |
+
base_dir: Base directory containing subject results
|
| 204 |
+
subjects: List of subjects that were processed
|
| 205 |
+
papers_per_subject: Number of papers processed per subject
|
| 206 |
+
|
| 207 |
+
"""
|
| 208 |
+
all_tasks = {}
|
| 209 |
+
all_databases = {}
|
| 210 |
+
all_software = {}
|
| 211 |
+
|
| 212 |
+
for subject, _ in subjects:
|
| 213 |
+
# Clean subject name for directory name
|
| 214 |
+
clean_subject = subject.lower().replace(" ", "_").replace("/", "_")
|
| 215 |
+
subject_dir = f"biorxiv_results_{clean_subject}_{papers_per_subject}"
|
| 216 |
+
summary_path = os.path.join(subject_dir, "frequency_summary.json")
|
| 217 |
+
|
| 218 |
+
try:
|
| 219 |
+
if os.path.exists(summary_path):
|
| 220 |
+
with open(summary_path) as f:
|
| 221 |
+
summary = json.load(f)
|
| 222 |
+
|
| 223 |
+
# Merge tasks
|
| 224 |
+
for task, count in summary.get("tasks", {}).items():
|
| 225 |
+
if task in all_tasks:
|
| 226 |
+
all_tasks[task] += count
|
| 227 |
+
else:
|
| 228 |
+
all_tasks[task] = count
|
| 229 |
+
|
| 230 |
+
# Merge databases
|
| 231 |
+
for db, count in summary.get("databases", {}).items():
|
| 232 |
+
if db in all_databases:
|
| 233 |
+
all_databases[db] += count
|
| 234 |
+
else:
|
| 235 |
+
all_databases[db] = count
|
| 236 |
+
|
| 237 |
+
# Merge software
|
| 238 |
+
for sw, count in summary.get("software", {}).items():
|
| 239 |
+
if sw in all_software:
|
| 240 |
+
all_software[sw] += count
|
| 241 |
+
else:
|
| 242 |
+
all_software[sw] = count
|
| 243 |
+
else:
|
| 244 |
+
print(f"Warning: Summary file not found for subject '{subject}'")
|
| 245 |
+
except Exception as e:
|
| 246 |
+
print(f"Error processing summary for subject '{subject}': {e}")
|
| 247 |
+
|
| 248 |
+
# Sort by frequency (descending)
|
| 249 |
+
all_tasks = dict(sorted(all_tasks.items(), key=lambda item: item[1], reverse=True))
|
| 250 |
+
all_databases = dict(sorted(all_databases.items(), key=lambda item: item[1], reverse=True))
|
| 251 |
+
all_software = dict(sorted(all_software.items(), key=lambda item: item[1], reverse=True))
|
| 252 |
+
|
| 253 |
+
# Create combined summary
|
| 254 |
+
combined_summary = {
|
| 255 |
+
"tasks": all_tasks,
|
| 256 |
+
"databases": all_databases,
|
| 257 |
+
"software": all_software,
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
# Save combined summary
|
| 261 |
+
combined_path = os.path.join(base_dir, "combined_summary.json")
|
| 262 |
+
os.makedirs(base_dir, exist_ok=True)
|
| 263 |
+
|
| 264 |
+
with open(combined_path, "w") as f:
|
| 265 |
+
json.dump(combined_summary, f, indent=2)
|
| 266 |
+
|
| 267 |
+
print(f"Combined summary saved to {combined_path}")
|
| 268 |
+
|
| 269 |
+
# Create CSV files for each category
|
| 270 |
+
tasks_df = pd.DataFrame(list(all_tasks.items()), columns=["Task", "Frequency"])
|
| 271 |
+
tasks_df.to_csv(os.path.join(base_dir, "tasks_frequency.csv"), index=False)
|
| 272 |
+
|
| 273 |
+
db_df = pd.DataFrame(list(all_databases.items()), columns=["Database", "Frequency"])
|
| 274 |
+
db_df.to_csv(os.path.join(base_dir, "databases_frequency.csv"), index=False)
|
| 275 |
+
|
| 276 |
+
sw_df = pd.DataFrame(list(all_software.items()), columns=["Software", "Frequency"])
|
| 277 |
+
sw_df.to_csv(os.path.join(base_dir, "software_frequency.csv"), index=False)
|
| 278 |
+
|
| 279 |
+
print(f"CSV summaries saved to {base_dir}")
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def main():
|
| 283 |
+
"""Main function to run the script."""
|
| 284 |
+
args = parse_arguments()
|
| 285 |
+
|
| 286 |
+
# Determine which subjects to process
|
| 287 |
+
subjects_to_process = args.subjects if args.subjects else BIORXIV_SUBJECTS
|
| 288 |
+
|
| 289 |
+
# Check which subjects have papers available
|
| 290 |
+
available_subjects = check_subject_availability(args.metadata_path, subjects_to_process)
|
| 291 |
+
|
| 292 |
+
if not available_subjects:
|
| 293 |
+
print("No subjects with available papers found. Exiting.")
|
| 294 |
+
return
|
| 295 |
+
|
| 296 |
+
if not args.summary_only:
|
| 297 |
+
# Process each subject
|
| 298 |
+
for subject, _count in tqdm(available_subjects, desc="Processing subjects"):
|
| 299 |
+
process_subject(subject, args)
|
| 300 |
+
|
| 301 |
+
# Add delay between subjects to avoid rate limiting
|
| 302 |
+
if args.delay > 0:
|
| 303 |
+
print(f"Waiting {args.delay} seconds before processing the next subject...")
|
| 304 |
+
time.sleep(args.delay)
|
| 305 |
+
|
| 306 |
+
# Combine summaries from all subjects
|
| 307 |
+
combine_summaries(args.output_dir, available_subjects, args.papers_per_subject)
|
| 308 |
+
|
| 309 |
+
print("All subjects processed successfully!")
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
if __name__ == "__main__":
|
| 313 |
+
import json # Import here to avoid circular import with the main script
|
| 314 |
+
|
| 315 |
+
main()
|