{"id":843,"date":"2026-05-03T04:31:07","date_gmt":"2026-05-02T20:31:07","guid":{"rendered":"https:\/\/connectword.dpdns.org\/?p=843"},"modified":"2026-05-03T04:31:07","modified_gmt":"2026-05-02T20:31:07","slug":"build-a-multi-agent-ai-workflow-for-biological-network-modeling-protein-interactions-metabolism-and-cell-signaling-simulation","status":"publish","type":"post","link":"https:\/\/connectword.dpdns.org\/?p=843","title":{"rendered":"Build a Multi-Agent AI Workflow for Biological Network Modeling, Protein Interactions, Metabolism, and Cell Signaling Simulation"},"content":{"rendered":"<p>In this tutorial, we build a multi-agent workflow for biological systems modeling and explore how different computational components work together inside one unified systems biology pipeline. We generate synthetic biological data, analyze gene regulatory structure, predict protein-protein interactions, optimize metabolic pathway activity, and simulate a dynamic cell signaling cascade, all within a Colab environment that remains practical and reproducible. We also use an OpenAI model to act as a principal investigator, synthesizing the outputs of all specialized agents into a single expert-style biological interpretation that connects regulation, interaction networks, metabolism, and signaling into a broader scientific story.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">import sys, subprocess, pkgutil\n\n\ndef _install_if_missing(packages):\n   missing = []\n   for p in packages:\n       import_name = p[\"import\"]\n       if pkgutil.find_loader(import_name) is None:\n           missing.append(p[\"pip\"])\n   if missing:\n       print(\"Installing:\", \", \".join(missing))\n       subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\"] + missing)\n\n\n_install_if_missing([\n   {\"pip\": \"openai\", \"import\": \"openai\"},\n   {\"pip\": \"numpy\", \"import\": \"numpy\"},\n   {\"pip\": \"pandas\", \"import\": \"pandas\"},\n   {\"pip\": \"matplotlib\", \"import\": \"matplotlib\"},\n   {\"pip\": \"networkx\", \"import\": \"networkx\"},\n   {\"pip\": \"scikit-learn\", \"import\": \"sklearn\"},\n])\n\n\nimport os\nimport json\nimport math\nimport textwrap\nimport random\nimport getpass\nfrom dataclasses import dataclass\nfrom typing import Dict, List, Tuple, Any\n\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport networkx as nx\n\n\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.metrics import roc_auc_score, average_precision_score\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\n\n\nfrom openai import OpenAI\n\n\nnp.random.seed(42)\nrandom.seed(42)\n\n\nOPENAI_API_KEY = None\n\n\ntry:\n   from google.colab import userdata\n   OPENAI_API_KEY = userdata.get(\"OPENAI_API_KEY\")\n   if OPENAI_API_KEY:\n       print(\"Loaded OPENAI_API_KEY from Colab Secrets.\")\nexcept Exception:\n   pass\n\n\nif not OPENAI_API_KEY:\n   try:\n       OPENAI_API_KEY = getpass.getpass(\"Enter OPENAI_API_KEY (hidden input): \").strip()\n   except Exception:\n       OPENAI_API_KEY = input(\"Enter OPENAI_API_KEY: \").strip()\n\n\nos.environ[\"OPENAI_API_KEY\"] = OPENAI_API_KEY\nclient = OpenAI(api_key=OPENAI_API_KEY)\n\n\nOPENAI_MODEL = \"gpt-4o-mini\"<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We prepare the Colab environment and make sure all required libraries are available before the workflow begins. We import the scientific computing, machine learning, graph analysis, plotting, and OpenAI libraries that support the full biological systems pipeline from start to finish. We also securely load the OpenAI API key either from Colab Secrets or hidden input, initialize the client, and define the model so the notebook is ready for later LLM-based synthesis.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def sigmoid(x):\n   return 1 \/ (1 + np.exp(-x))\n\n\ndef pretty(title: str, body: str, width: int = 100):\n   print(\"n\" + \"=\" * width)\n   print(title)\n   print(\"=\" * width)\n   print(body)\n\n\ndef safe_float(x):\n   try:\n       return float(x)\n   except Exception:\n       return None\n\n\ndef generate_gene_regulatory_network(n_genes: int = 14, edge_prob: float = 0.18):\n   genes = [f\"G{i+1}\" for i in range(n_genes)]\n   W = np.zeros((n_genes, n_genes))\n   for i in range(n_genes):\n       for j in range(n_genes):\n           if i != j and np.random.rand() &lt; edge_prob:\n               W[i, j] = np.random.uniform(-1.5, 1.5)\n   return genes, W\n\n\ndef simulate_gene_expression(W: np.ndarray, n_steps: int = 70, noise: float = 0.10):\n   n = W.shape[0]\n   X = np.zeros((n_steps, n))\n   X[0] = np.random.uniform(0.2, 0.8, size=n)\n   for t in range(1, n_steps):\n       signal = X[t-1] @ W\n       X[t] = sigmoid(signal + np.random.normal(0, noise, size=n))\n   return X\n\n\ndef generate_protein_features(n_proteins: int = 40, feature_dim: int = 10):\n   proteins = [f\"P{i+1}\" for i in range(n_proteins)]\n   features = np.random.normal(size=(n_proteins, feature_dim))\n   families = np.random.randint(0, 5, size=n_proteins)\n   localization = np.random.randint(0, 4, size=n_proteins)\n   return proteins, features, families, localization\n\n\ndef generate_ppi_dataset(proteins, features, families, localization):\n   rows = []\n   n = len(proteins)\n   hidden_w = np.random.normal(size=features.shape[1])\n   for i in range(n):\n       for j in range(i + 1, n):\n           fi, fj = features[i], features[j]\n           sim = np.dot(fi, fj) \/ (np.linalg.norm(fi) * np.linalg.norm(fj) + 1e-8)\n           fam_same = 1 if families[i] == families[j] else 0\n           loc_same = 1 if localization[i] == localization[j] else 0\n           feat = np.concatenate([\n               np.abs(fi - fj),\n               fi * fj,\n               [sim, fam_same, loc_same]\n           ])\n           score = 1.4 * sim + 1.0 * fam_same + 0.8 * loc_same + 0.15 * np.dot((fi + fj) \/ 2, hidden_w)\n           prob = sigmoid(score)\n           y = 1 if np.random.rand() &lt; prob else 0\n           rows.append((proteins[i], proteins[j], feat, y))\n   return rows\n\n\ndef generate_metabolic_network():\n   metabolites = [\"Glucose\", \"Pyruvate\", \"AcetylCoA\", \"ATP\", \"Biomass\", \"Lactate\", \"Ethanol\"]\n   reactions = [\n       {\"name\": \"R1_Glucose_Uptake\", \"yield_biomass\": 0.0, \"yield_atp\": 0.3, \"substrate_cost\": 1.0, \"oxygen_need\": 0.0},\n       {\"name\": \"R2_Glycolysis\",      \"yield_biomass\": 0.2, \"yield_atp\": 1.6, \"substrate_cost\": 0.7, \"oxygen_need\": 0.0},\n       {\"name\": \"R3_TCA\",             \"yield_biomass\": 1.0, \"yield_atp\": 2.4, \"substrate_cost\": 0.8, \"oxygen_need\": 1.4},\n       {\"name\": \"R4_Fermentation\",    \"yield_biomass\": 0.1, \"yield_atp\": 0.9, \"substrate_cost\": 0.4, \"oxygen_need\": 0.0},\n       {\"name\": \"R5_Ethanol_Path\",    \"yield_biomass\": 0.15,\"yield_atp\": 0.8, \"substrate_cost\": 0.5, \"oxygen_need\": 0.0},\n       {\"name\": \"R6_Biomass_Assembly\",\"yield_biomass\": 1.3, \"yield_atp\": -0.9,\"substrate_cost\": 0.6, \"oxygen_need\": 0.2},\n   ]\n   return metabolites, reactions\n\n\ndef simulate_cell_signaling(T=200, dt=0.05, ligand_level=1.2):\n   t = np.arange(0, T * dt, dt)\n   ligand = np.ones_like(t) * ligand_level\n\n\n   receptor = np.zeros_like(t)\n   kinase = np.zeros_like(t)\n   tf = np.zeros_like(t)\n   phosphatase = np.zeros_like(t)\n\n\n   receptor[0] = 0.05\n   kinase[0] = 0.02\n   tf[0] = 0.01\n   phosphatase[0] = 0.30\n\n\n   for i in range(1, len(t)):\n       dR = 1.6 * ligand[i-1] * (1 - receptor[i-1]) - 0.9 * receptor[i-1]\n       dK = 1.8 * receptor[i-1] * (1 - kinase[i-1]) - 1.1 * phosphatase[i-1] * kinase[i-1]\n       dTF = 1.4 * kinase[i-1] * (1 - tf[i-1]) - 0.55 * tf[i-1]\n       dP = 0.2 + 0.5 * tf[i-1] - 0.4 * phosphatase[i-1]\n\n\n       receptor[i] = np.clip(receptor[i-1] + dt * dR, 0, 1)\n       kinase[i] = np.clip(kinase[i-1] + dt * dK, 0, 1)\n       tf[i] = np.clip(tf[i-1] + dt * dTF, 0, 1)\n       phosphatase[i] = np.clip(phosphatase[i-1] + dt * dP, 0, 1.5)\n\n\n   return pd.DataFrame({\n       \"time\": t,\n       \"ligand\": ligand,\n       \"receptor_active\": receptor,\n       \"kinase_active\": kinase,\n       \"tf_active\": tf,\n       \"phosphatase\": phosphatase,\n   })<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We define the main helper utilities and all synthetic data generation functions that power the notebook\u2019s biological tasks. We create functions for gene regulatory network construction, gene expression simulation, protein feature generation, protein interaction dataset creation, metabolic network setup, and cell signaling dynamics, which together provide four distinct biological views for analysis. This snippet forms the computational backbone of the tutorial by creating the structured inputs that each specialized agent will later process and interpret.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">@dataclass\nclass AgentResult:\n   name: str\n   summary: Dict[str, Any]\n\n\nclass GeneRegulatoryNetworkAgent:\n   def run(self, genes, W, X) -&gt; AgentResult:\n       corr = np.corrcoef(X.T)\n       inferred_edges = []\n       true_edges = []\n       n = len(genes)\n\n\n       for i in range(n):\n           for j in range(n):\n               if i == j:\n                   continue\n               if abs(corr[i, j]) &gt; 0.35:\n                   inferred_edges.append((genes[i], genes[j], float(corr[i, j])))\n               if abs(W[i, j]) &gt; 1e-8:\n                   true_edges.append((genes[i], genes[j], float(W[i, j])))\n\n\n       centrality_graph = nx.DiGraph()\n       for gi in genes:\n           centrality_graph.add_node(gi)\n       for i in range(n):\n           for j in range(n):\n               if abs(W[i, j]) &gt; 1e-8:\n                   centrality_graph.add_edge(genes[i], genes[j], weight=float(W[i, j]))\n\n\n       out_deg = dict(centrality_graph.out_degree())\n       in_deg = dict(centrality_graph.in_degree())\n       hubs = sorted(out_deg.items(), key=lambda x: x[1], reverse=True)[:5]\n       sinks = sorted(in_deg.items(), key=lambda x: x[1], reverse=True)[:5]\n\n\n       dynamic_var = X.var(axis=0)\n       most_dynamic = sorted(zip(genes, dynamic_var), key=lambda x: x[1], reverse=True)[:5]\n\n\n       summary = {\n           \"num_genes\": n,\n           \"num_true_regulatory_edges\": len(true_edges),\n           \"num_inferred_associations\": len(inferred_edges),\n           \"top_hub_genes\": [{\"gene\": g, \"out_degree\": int(d)} for g, d in hubs],\n           \"top_sink_genes\": [{\"gene\": g, \"in_degree\": int(d)} for g, d in sinks],\n           \"most_dynamic_genes\": [{\"gene\": g, \"variance\": round(float(v), 4)} for g, v in most_dynamic],\n           \"sample_inferred_edges\": [\n               {\"source\": a, \"target\": b, \"association\": round(c, 3)}\n               for a, b, c in inferred_edges[:10]\n           ],\n           \"expression_tail_mean\": round(float(X[-10:].mean()), 4),\n       }\n       return AgentResult(name=\"GeneRegulatoryNetworkAgent\", summary=summary)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We define the shared result container and the gene regulatory network agent that analyzes regulatory structure and expression behavior. We use correlation-based association inference, true-edge extraction, degree-based graph analysis, and variance-based ranking to identify hub, sink, and highly dynamic genes across simulated expression trajectories. This gives us a network-level picture of how regulatory influence may be distributed in the system and helps us identify important candidate regulators for downstream interpretation.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">class ProteinInteractionPredictionAgent:\n   def run(self, ppi_rows) -&gt; AgentResult:\n       X = np.vstack([r[2] for r in ppi_rows])\n       y = np.array([r[3] for r in ppi_rows])\n\n\n       scaler = StandardScaler()\n       X_train_s = scaler.fit_transform(X_train)\n       X_test_s = scaler.transform(X_test)\n\n\n       clf = LogisticRegression(max_iter=1000)\n       clf.fit(X_train_s, y_train)\n       probs = clf.predict_proba(X_test_s)[:, 1]\n\n\n       auc = roc_auc_score(y_test, probs)\n       ap = average_precision_score(y_test, probs)\n\n\n       scored_pairs = []\n       Xt_full = scaler.transform(X)\n       full_probs = clf.predict_proba(Xt_full)[:, 1]\n       for (p1, p2, _, label), pr in zip(ppi_rows, full_probs):\n           scored_pairs.append((p1, p2, float(pr), int(label)))\n\n\n       top_candidates = sorted(scored_pairs, key=lambda x: x[2], reverse=True)[:10]\n       positive_rate = float(y.mean())\n\n\n       summary = {\n           \"num_pairs\": int(len(ppi_rows)),\n           \"positive_interaction_rate\": round(positive_rate, 4),\n           \"test_roc_auc\": round(float(auc), 4),\n           \"test_average_precision\": round(float(ap), 4),\n           \"top_predicted_interactions\": [\n               {\"protein_a\": a, \"protein_b\": b, \"pred_prob\": round(pr, 4), \"label\": lab}\n               for a, b, pr, lab in top_candidates\n           ],\n       }\n       return AgentResult(name=\"ProteinInteractionPredictionAgent\", summary=summary)\n\n\nclass MetabolicOptimizationAgent:\n   def run(self, reactions, oxygen_budget=3.5, substrate_budget=4.0):\n       best_score = -1e9\n       best_flux = None\n       trace = []\n\n\n       for _ in range(8000):\n           flux = np.random.dirichlet(np.ones(len(reactions))) * np.random.uniform(1.5, 5.0)\n           oxygen = sum(f[\"oxygen_need\"] * v for f, v in zip(reactions, flux))\n           substrate = sum(f[\"substrate_cost\"] * v for f, v in zip(reactions, flux))\n           atp = sum(f[\"yield_atp\"] * v for f, v in zip(reactions, flux))\n           biomass = sum(f[\"yield_biomass\"] * v for f, v in zip(reactions, flux))\n\n\n           penalty = 0.0\n           if oxygen &gt; oxygen_budget:\n               penalty += 6.0 * (oxygen - oxygen_budget)\n           if substrate &gt; substrate_budget:\n               penalty += 6.0 * (substrate - substrate_budget)\n\n\n           score = 2.2 * biomass + 0.6 * atp - penalty\n           trace.append(score)\n\n\n           if score &gt; best_score:\n               best_score = score\n               best_flux = {\n                   \"oxygen\": oxygen,\n                   \"substrate\": substrate,\n                   \"atp\": atp,\n                   \"biomass\": biomass,\n                   \"fluxes\": {reactions[i][\"name\"]: float(flux[i]) for i in range(len(reactions))}\n               }\n\n\n       ranked_fluxes = sorted(best_flux[\"fluxes\"].items(), key=lambda x: x[1], reverse=True)\n\n\n       summary = {\n           \"oxygen_budget\": oxygen_budget,\n           \"substrate_budget\": substrate_budget,\n           \"best_objective_score\": round(float(best_score), 4),\n           \"best_biomass\": round(float(best_flux[\"biomass\"]), 4),\n           \"best_atp\": round(float(best_flux[\"atp\"]), 4),\n           \"oxygen_used\": round(float(best_flux[\"oxygen\"]), 4),\n           \"substrate_used\": round(float(best_flux[\"substrate\"]), 4),\n           \"dominant_reactions\": [\n               {\"reaction\": name, \"flux\": round(val, 4)} for name, val in ranked_fluxes[:6]\n           ],\n       }\n       return AgentResult(name=\"MetabolicOptimizationAgent\", summary=summary), trace<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We define the protein interaction prediction agent and the metabolic optimization agent, which together expand the analysis beyond regulation into interaction biology and pathway allocation. We train a logistic regression classifier on synthetic pairwise protein features to estimate interaction probabilities, evaluate predictive performance, and rank the strongest candidate protein pairs. We also run a randomized flux search under oxygen and substrate constraints to identify metabolically favorable reaction allocations, allowing us to study how the system balances biomass growth, ATP production, and resource limitations.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">class CellSignalingSimulationAgent:\n   def run(self, df_signal: pd.DataFrame) -&gt; AgentResult:\n       peak_receptor = float(df_signal[\"receptor_active\"].max())\n       peak_kinase = float(df_signal[\"kinase_active\"].max())\n       peak_tf = float(df_signal[\"tf_active\"].max())\n\n\n       t_receptor = float(df_signal.loc[df_signal[\"receptor_active\"].idxmax(), \"time\"])\n       t_kinase = float(df_signal.loc[df_signal[\"kinase_active\"].idxmax(), \"time\"])\n       t_tf = float(df_signal.loc[df_signal[\"tf_active\"].idxmax(), \"time\"])\n\n\n       final_state = df_signal.iloc[-1].to_dict()\n\n\n       summary = {\n           \"peak_receptor_activity\": round(peak_receptor, 4),\n           \"peak_kinase_activity\": round(peak_kinase, 4),\n           \"peak_tf_activity\": round(peak_tf, 4),\n           \"time_to_peak_receptor\": round(t_receptor, 4),\n           \"time_to_peak_kinase\": round(t_kinase, 4),\n           \"time_to_peak_tf\": round(t_tf, 4),\n           \"final_state\": {k: round(float(v), 4) for k, v in final_state.items()},\n       }\n       return AgentResult(name=\"CellSignalingSimulationAgent\", summary=summary)\n\n\nclass PrincipalInvestigatorAgent:\n   def __init__(self, client, model=OPENAI_MODEL):\n       self.client = client\n       self.model = model\n\n\n   def synthesize(self, results: List[AgentResult]) -&gt; str:\n       payload = {r.name: r.summary for r in results}\n\n\n       prompt = f\"\"\"\nYou are a principal investigator in computational systems biology.\n\n\nGiven the outputs of four specialized AI agents:\n1. gene regulatory network analysis\n2. protein interaction prediction\n3. metabolic pathway optimization\n4. cell signaling simulation\n\n\nWrite a rigorous but readable report with these sections:\n- Executive Summary\n- Key Findings by Agent\n- Cross-System Biological Interpretation\n- Hypotheses Worth Testing in Wet Lab\n- Model Limitations\n- Next Computational Extensions\n\n\nUse concise scientific language.\nDo not fabricate datasets beyond what is shown.\nWhen useful, connect regulation, signaling, metabolism, and protein interactions into a single systems biology story.\n\n\nAgent outputs:\n{json.dumps(payload, indent=2)}\n\"\"\"\n\n\n       try:\n           resp = self.client.chat.completions.create(\n               model=self.model,\n               messages=[\n                   {\"role\": \"user\", \"content\": prompt},\n               ],\n               temperature=0.4,\n           )\n           return resp.choices[0].message.content\n       except Exception as e:\n           return f\"OpenAI synthesis failed: {e}\"\n\n\ngenes, W = generate_gene_regulatory_network(n_genes=14, edge_prob=0.20)\nX_expr = simulate_gene_expression(W, n_steps=80, noise=0.08)\ngrn_agent = GeneRegulatoryNetworkAgent()\ngrn_result = grn_agent.run(genes, W, X_expr)\n\n\nproteins, prot_features, prot_families, prot_localization = generate_protein_features(n_proteins=40, feature_dim=10)\nppi_rows = generate_ppi_dataset(proteins, prot_features, prot_families, prot_localization)\nppi_agent = ProteinInteractionPredictionAgent()\nppi_result = ppi_agent.run(ppi_rows)\n\n\nmetabolites, reactions = generate_metabolic_network()\nmet_agent = MetabolicOptimizationAgent()\nmet_result, met_trace = met_agent.run(reactions, oxygen_budget=3.5, substrate_budget=4.2)\n\n\ndf_signal = simulate_cell_signaling(T=220, dt=0.05, ligand_level=1.2)\nsig_agent = CellSignalingSimulationAgent()\nsig_result = sig_agent.run(df_signal)\n\n\nall_results = [grn_result, ppi_result, met_result, sig_result]\n\n\nfor r in all_results:\n   pretty(r.name, json.dumps(r.summary, indent=2))\n\n\nfig = plt.figure(figsize=(18, 14))\n\n\nax1 = plt.subplot(2, 2, 1)\nim = ax1.imshow(W, cmap=\"coolwarm\", aspect=\"auto\")\nax1.set_title(\"Gene Regulatory Weight Matrix\")\nax1.set_xticks(range(len(genes)))\nax1.set_yticks(range(len(genes)))\nax1.set_xticklabels(genes, rotation=90)\nax1.set_yticklabels(genes)\nplt.colorbar(im, ax=ax1, fraction=0.046, pad=0.04)\n\n\nax2 = plt.subplot(2, 2, 2)\nfor i in range(min(6, X_expr.shape[1])):\n   ax2.plot(X_expr[:, i], label=genes[i])\nax2.set_title(\"Sample Gene Expression Dynamics\")\nax2.set_xlabel(\"Time step\")\nax2.set_ylabel(\"Expression\")\nax2.legend(loc=\"upper right\", fontsize=8)\n\n\nax3 = plt.subplot(2, 2, 3)\nax3.plot(df_signal[\"time\"], df_signal[\"receptor_active\"], label=\"Receptor\")\nax3.plot(df_signal[\"time\"], df_signal[\"kinase_active\"], label=\"Kinase\")\nax3.plot(df_signal[\"time\"], df_signal[\"tf_active\"], label=\"Transcription Factor\")\nax3.plot(df_signal[\"time\"], df_signal[\"phosphatase\"], label=\"Phosphatase\")\nax3.set_title(\"Cell Signaling Simulation\")\nax3.set_xlabel(\"Time\")\nax3.set_ylabel(\"Activity\")\nax3.legend()\n\n\nax4 = plt.subplot(2, 2, 4)\nax4.plot(met_trace)\nax4.set_title(\"Metabolic Search Objective Trace\")\nax4.set_xlabel(\"Iteration\")\nax4.set_ylabel(\"Objective score\")\n\n\nplt.tight_layout()\nplt.show()\n\n\nG_grn = nx.DiGraph()\nfor g in genes:\n   G_grn.add_node(g)\nfor i in range(len(genes)):\n   for j in range(len(genes)):\n       if abs(W[i, j]) &gt; 0.4:\n           G_grn.add_edge(genes[i], genes[j], weight=W[i, j])\n\n\nplt.figure(figsize=(10, 8))\npos = nx.spring_layout(G_grn, seed=42)\nedge_colors = [\"green\" if G_grn[u][v][\"weight\"] &gt; 0 else \"red\" for u, v in G_grn.edges()]\nnx.draw_networkx(G_grn, pos, with_labels=True, node_size=900, font_size=9, arrows=True, edge_color=edge_colors)\nplt.title(\"Gene Regulatory Network Graph (green=activation, red=repression)\")\nplt.axis(\"off\")\nplt.show()\n\n\ntop_ppi = ppi_result.summary[\"top_predicted_interactions\"][:12]\nG_ppi = nx.Graph()\nfor row in top_ppi:\n   a, b, p = row[\"protein_a\"], row[\"protein_b\"], row[\"pred_prob\"]\n   G_ppi.add_edge(a, b, weight=p)\n\n\nplt.figure(figsize=(10, 8))\npos = nx.spring_layout(G_ppi, seed=7)\nwidths = [2 + 4 * G_ppi[u][v][\"weight\"] for u, v in G_ppi.edges()]\nnx.draw_networkx(G_ppi, pos, with_labels=True, node_size=1000, font_size=9, width=widths)\nplt.title(\"Top Predicted Protein Interaction Subnetwork\")\nplt.axis(\"off\")\nplt.show()\n\n\ngrn_table = pd.DataFrame(grn_result.summary[\"most_dynamic_genes\"])\nppi_table = pd.DataFrame(ppi_result.summary[\"top_predicted_interactions\"])\nmet_table = pd.DataFrame(met_result.summary[\"dominant_reactions\"])\nsig_table = pd.DataFrame([sig_result.summary])\n\n\npretty(\"Most Dynamic Genes\", grn_table.to_string(index=False))\npretty(\"Top Predicted PPIs\", ppi_table.to_string(index=False))\npretty(\"Dominant Metabolic Reactions\", met_table.to_string(index=False))\n\n\npi_agent = PrincipalInvestigatorAgent(client=client, model=OPENAI_MODEL)\nfinal_report = pi_agent.synthesize(all_results)\n\n\npretty(\"OPENAI SYSTEMS BIOLOGY REPORT\", final_report)\n\n\nartifact = {\n   \"grn\": grn_result.summary,\n   \"ppi\": ppi_result.summary,\n   \"metabolic\": met_result.summary,\n   \"signaling\": sig_result.summary,\n   \"llm_report\": final_report,\n}\n\n\nwith open(\"bio_agents_tutorial_results.json\", \"w\") as f:\n   json.dump(artifact, f, indent=2)\n\n\nprint(\"nSaved results to: bio_agents_tutorial_results.json\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We define the cell signaling simulation agent and the principal investigator agent, and then execute the complete end-to-end workflow. We run all four biological modules, print structured outputs, generate plots and network visualizations, build tidy summary tables, and finally use the OpenAI model to write an expert-style report that integrates the findings across all subsystems. We bring everything together into a complete pipeline for biological systems modeling. It shows how multi-agent AI can support scientific interpretation, visualization, and hypothesis generation.<\/p>\n<p>In conclusion, we created a complete computational biology workflow that demonstrates how agent-based AI can be used to study multiple layers of biological organization in a structured and interpretable way. We moved from data generation to modeling, optimization, simulation, visualization, and final scientific synthesis, which helps us see how specialized agents can collaborate to produce richer biological insight than any single isolated analysis. At the end, we have a strong foundation for extending this notebook toward more realistic omics datasets, experimental priors, mechanistic constraints, and deeper biological hypothesis generation for advanced systems biology research.<\/p>\n<hr class=\"wp-block-separator aligncenter has-alpha-channel-opacity is-style-wide\" \/>\n<p>Check out\u00a0the\u00a0<strong><a href=\"https:\/\/github.com\/Marktechpost\/AI-Agents-Projects-Tutorials\/blob\/main\/Agentic%20Workflows\/ai_agents_biological_systems_modeling_Marktechpost(1).ipynb\" target=\"_blank\" rel=\"noreferrer noopener\">Full Codes with Notebook<\/a><\/strong>.<strong>\u00a0<\/strong>Also,\u00a0feel free to follow us on\u00a0<strong><a href=\"https:\/\/x.com\/intent\/follow?screen_name=marktechpost\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Twitter<\/mark><\/a><\/strong>\u00a0and don\u2019t forget to join our\u00a0<strong><a href=\"https:\/\/www.reddit.com\/r\/machinelearningnews\/\" target=\"_blank\" rel=\"noreferrer noopener\">130k+ ML SubReddit<\/a><\/strong>\u00a0and Subscribe to\u00a0<strong><a href=\"https:\/\/www.aidevsignals.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">our Newsletter<\/a><\/strong>. Wait! are you on telegram?\u00a0<strong><a href=\"https:\/\/t.me\/machinelearningresearchnews\" target=\"_blank\" rel=\"noreferrer noopener\">now you can join us on telegram as well.<\/a><\/strong><\/p>\n<p>Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.?\u00a0<strong><a href=\"https:\/\/forms.gle\/MTNLpmJtsFA3VRVd9\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Connect with us<\/mark><\/a><\/strong><\/p>\n<p>The post <a href=\"https:\/\/www.marktechpost.com\/2026\/05\/02\/build-a-multi-agent-ai-workflow-for-biological-network-modeling-protein-interactions-metabolism-and-cell-signaling-simulation\/\">Build a Multi-Agent AI Workflow for Biological Network Modeling, Protein Interactions, Metabolism, and Cell Signaling Simulation<\/a> appeared first on <a href=\"https:\/\/www.marktechpost.com\/\">MarkTechPost<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this tutorial, we build a m&hellip;<\/p>\n","protected":false},"author":1,"featured_media":29,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-843","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=843"}],"version-history":[{"count":0,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/843\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/media\/29"}],"wp:attachment":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}