Notes pratiques : Création d’un agent de codage dans le cloud privé : OpenCode + un LLM local
Guide pas à pas des notes pratiques : Création d’un agent de codage dans le cloud privé : OpenCode + un LLM local : contrats, vérifications et emplacements de code prêts à l’emploi pour les équipes utilisant ce modèle.
Sources: Building a Private Cloud Coding Agent: OpenCode + a Local LLM, Running on Serverless GPUs
Les notes suivantes reconstituent une approche pratique pour « Construire un agent de codage en cloud privé : OpenCode + un LLM local, exécuté sur des GPU serverless ». L’accent est mis sur les contrats, les vérifications et les placeholders de code interchangeables, plutôt que sur une présentation motivante. Lors de la phase d’aperçu, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts volumineux. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un processus embrouillé.
Pourquoi cette architecture
Pourquoi cette architecture fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez toute completion partielle silencieuse. Fixez un budget en tokens par tour et par session. Les outils agents élargissent lourdement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues.
Les éléments de base
Ces éléments de base fonctionnent le mieux lorsqu’ils sont considérés comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de la démonstration aux environnements partagés. Fixez un budget de jetons par tour et par session : les outils agents élargissent rapidement le contexte, et des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues.
Modal
Modal fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Fixez des limites de tokens par tour et par session. Les outils agents élargissent fortement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues. Modal fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité et non vers un processus embrouillé.
Ollama
Pour l’étape Ollama, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute complétion partielle silencieuse. Séparez la construction du client du cycle de messages afin que les fournisseurs puissent être remplacés sans avoir à réécrire la machine d’état de la conversation.
OpenCode
Pour l’étape OpenCode, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le parcours passe d’un environnement de démonstration à des environnements partagés. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
Le tunnel
Pour l’étape du tunnel, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil. Pour l’étape du tunnel, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un pipeline embrouillé.
Prérequis et configuration
Lors de la phase des prérequis et de l’installation, notez d’abord le contrat : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès, et refusez les terminations partielles silencieuses. Cachez les instructions du système stable ainsi que les schémas des outils. L’envoi répété d’un préambule identique est une cause fréquente de problèmes.
pip install modal
modal setup
Conception de l’image conteneurisée
Lors de l’étape de conception de l’image conteneurisée, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le projet passe de l’environnement de démonstration à des environnements partagés. Mémorisez les instructions système stables et les schémas des outils. Envoyer à nouveau un préambule identique est une cause fréquente de gaspillage.
import modal
image = (
modal.Image.from_registry(
"nvidia/cuda:13.0.0-devel-ubuntu24.04",
add_python="3.12",
)
.apt_install(
"curl",
"git",
"ca-certificates",
"zstd",
)
.run_commands(
"curl -fsSL https://ollama.com/install.sh | sh",
)
.run_commands(
"curl -fsSL https://opencode.ai/install | bash",
)
.env(
{
"OLLAMA_MODELS": "/models/.ollama/models",
"OLLAMA_HOST": "127.0.0.1:11434",
"PATH": "/root/.opencode/bin:/usr/local/bin:/usr/bin:/bin",
}
)
)
Le script de démarrage : orchestrer Ollama et OpenCode
Lorsque vous travaillez sur l’étape d’orchestration du script de démarrage, notez d’abord les exigences : entrées requises, signal de succès et conséquences en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du système. Enregistrez l’ID de la requête, l’ID du modèle et le temps de réponse pour chaque appel. Sans ces traces, les erreurs intermittentes du fournisseur sont prises pour des bugs de l’application. Lorsque vous travaillez sur l’étape d’orchestration du script de démarrage, notez d’abord les exigences : entrées requises, signal de succès et conséquences en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une responsabilité précise plutôt qu’un processus embrouillé.
Étape 1 — Lancer Ollama en arrière-plan, vérifier que le processus n’a pas cessé immédiatement
L’étape 1 consistant à lancer Ollama fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec et des notes de réversion avant d’élargir le périmètre. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définez des vérifications de succès et refusez toute complétion partielle silencieuse. Fixez l’interpréteur ainsi que le fichier de verrouillage des dépendances avant d’implémenter la boucle. Les écarts entre l’ordinateur portable et les environnements CI sont la cause la plus fréquente d’interruptions silencieuses dans les démos API.
ollama serve > /tmp/ollama.log 2>&1 &
OLLAMA_PID=$!
sleep 3
if ! kill -0 "$OLLAMA_PID" 2>/dev/null; then
echo "OLLAMA FAILED TO START"
cat /tmp/ollama.log
exit 1
fi
Étape 2 — Vérifier en continu jusqu’à ce que l’API serve réellement des données, et non se contente de fonctionner
Le sondage de l’Étape 2 jusqu’à la phase finale fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de la démonstration aux environnements partagés. Fixez un budget de jetons par tour et par session : les outils agents élargissent rapidement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues.
READY=0
for i in $(seq 1 120); do
if curl -sf http://127.0.0.1:11434/api/tags > /dev/null 2>&1; then
READY=1
echo "Ollama is ready."
break
fi
sleep 1
done
if [ "$READY" != "1" ]; then
echo "Ollama failed to start."
cat /tmp/ollama.log
exit 1
fi
Étape 3 — Téléchargez le modèle, mais uniquement s’il n’y est pas déjà
La phase « Step 3 Pull the stage » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Conservez les configurations en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.
if ollama list | grep -F "qwen3-coder" > /dev/null 2>&1; then
echo "Model already exists:"
ollama list
else
echo "Downloading: qwen3-coder:30b"
ollama pull "qwen3-coder:30b"
fi
La phase « Step 3 Pull the stage » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’erreur doit pointer vers une seule responsabilité et non vers un processus embrouillé.
Étape 4 — Cloner le répertoire, avec un véritable parcours d’échec
Pour l’Étape 4, cloner l’étape consiste à définir les entrées, le responsable de l’étape ainsi que les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminations partielles silencieuses. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
if [ ! -d /workspace/.git ]; then
if GIT_TERMINAL_PROMPT=0 git clone \
--branch "main" \
--depth 1 \
"https://github.com/yourname/yourrepo.git" \
/workspace
then
echo "Clone succeeded."
else
echo "WARNING: git clone failed. Continuing without the repo."
mkdir -p /workspace
fi
else
echo "Repository already exists."
fi
Étape 5 — Écrire la configuration d’OpenCode
Pour l’étape 5 « Écrire OpenCode », définissez les entrées, le responsable de l’étape ainsi que les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce du coût permet d’éviter des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
mkdir -p /root/.config/opencode
cat > /root/.config/opencode/opencode.json <<'EOF'
{
"$schema": "https://opencode.ai/config.json",
"model": "ollama/qwen3-coder:30b",
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama - Qwen3 Coder 30B",
"options": {
"baseURL": "http://127.0.0.1:11434/v1"
},
"models": {
"qwen3-coder:30b": {
"name": "Qwen3-Coder 30B",
"limit": {
"context": 32768,
"output": 16384
}
}
}
}
}
}
EOF
Étape 6 — Lancer OpenCode en premier plan
Pour l’étape 6 « Start OpenCode », définissez les entrées, le responsable de l’étape ainsi que les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
exec opencode serve \
--hostname 0.0.0.0 \
--port 4096 \
--print-logs
Pour l’étape 6 « Start OpenCode », il convient de définir les entrées, le responsable de l’étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit relier à une seule responsabilité et non à un processus embrouillé.
Stockage des poids du modèle
Lors de la phase de gestion des poids du modèle persistants, écrivez d’abord le contrat : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès, et refusez toute exécution partielle silencieuse. Cachez les instructions système stables ainsi que les schémas des outils. L’envoi répété d’un préambule identique est une cause fréquente de gaspillage.
volume = modal.Volume.from_name(
"opencode-ollama-models",
create_if_missing=True,
)
volumes={
"/models": volume,
},
Création du sandbox
Lors de la phase de création du sandbox, notez d’abord le contrat : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le projet passe de l’environnement de démonstration à des environnements partagés. Mémorisez les instructions système stables et les schémas des outils. Envoyer à nouveau un préambule identique est une cause fréquente de gaspillage.
def create_sandbox():
script = startup_script()
secrets = []
if GITHUB_REPO_IS_PRIVATE:
secrets.append(modal.Secret.from_name(GITHUB_SECRET_NAME))
sandbox = modal.Sandbox.create(
"bash", "-lc", script,
app=app,
image=image,
gpu="H100",
cpu=8,
memory=32768,
secrets=secrets,
timeout=12 * 60 * 60,
idle_timeout=300,
volumes={"/models": volume},
encrypted_ports=[OPENCODE_PORT],
workdir="/workspace",
)
return sandbox
Gestion des dépôts privés
Lors de la phase Gestion des répertoires privés, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du système. Mémorisez les instructions stables du système ainsi que les schémas des outils. Envoyer à nouveau un préambule identique est une cause fréquente de gaspillage. Lors de la phase Gestion des répertoires privés, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit correspondre à une seule responsabilité et non à un processus embrouillé.
modal secret create github-token GITHUB_TOKEN=ghp_your_token_here
if GITHUB_REPO_IS_PRIVATE:
secrets.append(modal.Secret.from_name("github-token"))
clone_url = f"https://${{GITHUB_TOKEN}}@github.com/{GITHUB_REPO}.git"
Le point d’entrée local : maintenir le sandbox actif
La phase de maintien du point d’entrée local fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement exemplaire, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définites des critères de succès et refusez toute complétion partielle silencieuse. Fixez un budget en tokens par tour et par session. Les outils agents élargissent lourdement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues.
try:
while True:
time.sleep(5)
returncode = sandbox.poll()
if returncode is not None:
print(f"Sandbox process exited with code {returncode}.")
break
except KeyboardInterrupt:
print("Ctrl+C received. Terminating sandbox...")
sandbox.terminate()
modal run --detach opencode.py
modal shell sb-abc123xyz
La mise en œuvre complète
L’implémentation complète fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript parfait, un cas d’échec et la note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le projet passe de la démonstration aux environnements partagés. Fixez un budget de tokens par tour et par session : les outils agents élargissent lourdement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues.
import time
import modal
# ============================================================
# Configuration
# ============================================================
APP_NAME = "opencode-ollama"
MODEL = "qwen3-coder:30b"
OPENCODE_PORT = 4096
OLLAMA_PORT = 11434
VOLUME_NAME = "opencode-ollama-models"
# Set this to the repo you want cloned into the sandbox.
# Leave as None or "" to skip cloning entirely.
GITHUB_REPO = "yourusername/your-repository"
GITHUB_BRANCH = "main"
# If GITHUB_REPO is private, create a Modal Secret containing
# a GitHub Personal Access Token (fine-grained, read-only, scoped
# to this one repo) with:
# modal secret create github-token GITHUB_TOKEN=ghp_xxx
# and set this to True.
GITHUB_REPO_IS_PRIVATE = False
GITHUB_SECRET_NAME = "github-token"
# ============================================================
# Modal app + persistent volume
# ============================================================
app = modal.App(APP_NAME)
volume = modal.Volume.from_name(
VOLUME_NAME,
create_if_missing=True,
)
# ============================================================
# Container image
# ============================================================
image = (
modal.Image.from_registry(
"nvidia/cuda:13.0.0-devel-ubuntu24.04",
add_python="3.12",
)
.apt_install(
"curl",
"git",
"ca-certificates",
"zstd",
)
.run_commands(
"curl -fsSL https://ollama.com/install.sh | sh",
)
.run_commands(
"curl -fsSL https://opencode.ai/install | bash",
)
.env(
{
"OLLAMA_MODELS": "/models/.ollama/models",
"OLLAMA_HOST": "127.0.0.1:11434",
"PATH": "/root/.opencode/bin:/usr/local/bin:/usr/bin:/bin",
}
)
)
# ============================================================
# Sandbox startup script
# ============================================================
def startup_script():
repo_setup = ""
if GITHUB_REPO:
if GITHUB_REPO_IS_PRIVATE:
clone_url = (
f"https://${{GITHUB_TOKEN}}@github.com/{GITHUB_REPO}.git"
)
else:
clone_url = f"https://github.com/{GITHUB_REPO}.git"
repo_setup = f"""
echo "=============================================="
echo "Cloning repository"
echo "=============================================="
nvidia-smi || true
if [ ! -d /workspace/.git ]; then
if GIT_TERMINAL_PROMPT=0 git clone \\
--branch "{GITHUB_BRANCH}" \\
--depth 1 \\
"{clone_url}" \\
/workspace
then
echo "Clone succeeded."
else
echo ""
echo "=============================================="
echo "WARNING: git clone failed."
echo "=============================================="
echo "This is usually because the repository is"
echo "private (set GITHUB_REPO_IS_PRIVATE = True and"
echo "attach a token) or the repo/branch name is"
echo "wrong. Continuing without the repo so OpenCode"
echo "still starts."
echo ""
mkdir -p /workspace
fi
else
echo "Repository already exists."
fi
"""
else:
repo_setup = """
echo "=============================================="
echo "No GitHub repository configured"
echo "=============================================="
mkdir -p /workspace
"""
return f"""
#!/bin/bash
set -e
echo "=============================================="
echo "Modal OpenCode + Ollama"
echo "=============================================="
echo "Model: {MODEL}"
echo "OpenCode port: {OPENCODE_PORT}"
echo "Ollama port: {OLLAMA_PORT}"
# ------------------------------------------------------------
# Ollama model directory
# ------------------------------------------------------------
mkdir -p /models/.ollama/models
export OLLAMA_MODELS=/models/.ollama/models
export OLLAMA_HOST=127.0.0.1:11434
# ------------------------------------------------------------
# Start Ollama
# ------------------------------------------------------------
echo ""
echo "Starting Ollama..."
ollama serve \\
> /tmp/ollama.log \\
2>&1 &
OLLAMA_PID=$!
sleep 3
if ! kill -0 "$OLLAMA_PID" 2>/dev/null; then
echo ""
echo "=============================================="
echo "OLLAMA FAILED TO START"
echo "=============================================="
echo ""
cat /tmp/ollama.log
echo ""
exit 1
fi
echo "Ollama PID: $OLLAMA_PID"
# ------------------------------------------------------------
# Wait for Ollama to be ready
# ------------------------------------------------------------
echo ""
echo "Waiting for Ollama..."
READY=0
for i in $(seq 1 120); do
if curl -sf \\
http://127.0.0.1:11434/api/tags \\
> /dev/null 2>&1
then
READY=1
echo "Ollama is ready."
break
fi
sleep 1
done
if [ "$READY" != "1" ]; then
echo ""
echo "Ollama failed to start."
echo ""
cat /tmp/ollama.log
exit 1
fi
# ------------------------------------------------------------
# Pull the model (skipped if already present in the volume)
# ------------------------------------------------------------
echo ""
echo "=============================================="
echo "Checking Ollama model"
echo "=============================================="
if ollama list | grep -F \\
"qwen3-coder" \\
> /dev/null 2>&1
then
echo "Model already exists:"
ollama list
else
echo ""
echo "Model not found."
echo ""
echo "Downloading:"
echo " {MODEL}"
echo ""
echo "This can take some time on first startup."
echo ""
ollama pull "{MODEL}"
fi
echo ""
echo "=============================================="
echo "Available Ollama models"
echo "=============================================="
ollama list
# ------------------------------------------------------------
# Repository
# ------------------------------------------------------------
{repo_setup}
cd /workspace
# ------------------------------------------------------------
# OpenCode configuration
# ------------------------------------------------------------
mkdir -p /root/.config/opencode
cat > /root/.config/opencode/opencode.json <<'EOF'
{{
"$schema": "https://opencode.ai/config.json",
"model": "ollama/{MODEL}",
"provider": {{
"ollama": {{
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama - Qwen3 Coder 30B",
"options": {{
"baseURL": "http://127.0.0.1:11434/v1"
}},
"models": {{
"{MODEL}": {{
"name": "Qwen3-Coder 30B",
"limit": {{
"context": 32768,
"output": 16384
}}
}}
}}
}}
}}
}}
EOF
echo ""
echo "=============================================="
echo "OpenCode configuration"
echo "=============================================="
cat /root/.config/opencode/opencode.json
# ------------------------------------------------------------
# Start OpenCode
# ------------------------------------------------------------
echo ""
echo "=============================================="
echo "Starting OpenCode server"
echo "=============================================="
exec opencode serve \\
--hostname 0.0.0.0 \\
--port {OPENCODE_PORT} \\
--print-logs
"""
# ============================================================
# Create Sandbox
# ============================================================
def create_sandbox():
script = startup_script()
secrets = []
if GITHUB_REPO_IS_PRIVATE:
secrets.append(modal.Secret.from_name(GITHUB_SECRET_NAME))
sandbox = modal.Sandbox.create(
"bash",
"-lc",
script,
app=app,
image=image,
gpu="H100",
cpu=8,
memory=32768,
secrets=secrets,
timeout=12 * 60 * 60,
idle_timeout=300,
volumes={
"/models": volume,
},
encrypted_ports=[
OPENCODE_PORT,
],
workdir="/workspace",
)
return sandbox
# ============================================================
# Local entrypoint
# ============================================================
@app.local_entrypoint()
def main():
print()
print("==============================================")
print("Creating Modal Sandbox")
print("==============================================")
print()
sandbox = create_sandbox()
print()
print("Sandbox created:")
print(sandbox.object_id)
print()
print("Waiting for OpenCode server...")
print()
time.sleep(5)
tunnel_url = None
try:
tunnels = sandbox.tunnels()
tunnel = tunnels[OPENCODE_PORT]
tunnel_url = tunnel.url
print()
print("==============================================")
print("OPEN CODE IS READY")
print("==============================================")
print()
print("Sandbox ID (save this to reconnect):")
print(sandbox.object_id)
print()
print("Browser:")
print()
print(tunnel_url)
print()
print("Your OpenCode server is backed by:")
print()
print(" Ollama")
print(" Qwen3-Coder 30B")
print()
print("Ollama endpoint (inside the sandbox only):")
print("http://127.0.0.1:11434")
print()
print("==============================================")
print()
except Exception as e:
print()
print("Sandbox started, but tunnel information")
print("could not be retrieved automatically.")
print()
print(f"Error: {e}")
print()
print("The Sandbox ID is:")
print(sandbox.object_id)
print("Sandbox is running. Press Ctrl+C to stop it.")
print("(Run with `modal run --detach opencode.py` to keep")
print(" it alive after you disconnect.)")
print()
try:
while True:
time.sleep(5)
returncode = sandbox.poll()
if returncode is not None:
print(f"Sandbox process exited with code {returncode}.")
break
except KeyboardInterrupt:
print()
print("Ctrl+C received. Terminating sandbox...")
sandbox.terminate()
print("Sandbox terminated.")
Lancer l’application
Celui-ci fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de rollback avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Fixez des limites de tokens par tour et par session. Les outils agents élargissent fortement le contexte ; des plafonds stricts empêchent que les démonstrations ne se transforment en factures inattendues. Celui-ci fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de rollback avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé.
modal run opencode.py
==============================================
OPEN CODE IS READY
==============================================
Sandbox ID (save this to reconnect):
sb-abc123xyz
Browser:
https://your-generated-subdomain-4096.modal.host
modal run --detach opencode.py
modal shell sb-abc123xyz
sandbox = modal.Sandbox.from_id("sb-abc123xyz")
sandbox.terminate()
Étendre le système
Pendant l’étape d’extension du système, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminations partielles silencieuses. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
Considérations de sécurité
Pendant l’étape des considérations de sécurité, définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe d’un environnement de démonstration à des environnements partagés. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
Conclusion
Pour l’étape de conclusion, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil. Pour l’étape de conclusion, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un pipeline embrouillé.
Liste de contrôle opérationnelle
Pour l’étape de la liste de contrôle opérationnelle, définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché.
Dokumentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’une mise en forme ultérieure.
Préférez des sorties structurées avec validation de schéma plutôt que du texte libre lorsque l’étape suivante consiste en du code ou une appel à outil.
Créez un point de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel au LLM lorsque l’opérateur réessaie un nœud ultérieur.
Fixez les versions des dépendances et enregistrez le digest de l’image ayant servi à exécuter la démonstration. La reproductibilité vaut mieux que les connaissances propres à un groupe.
Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce du coût permet d’éviter des factures inattendues lorsque le parcours passe de l’environnement de démonstration à des environnements partagés.
Au préalable de promouvoir l’ensemble technique, figez les versions, conservez une transcription exemplaire pour le parcours critique, et vérifiez les étapes de réversion. Les environnements partagés nécessitent des limites de fréquence, des contrôles d’attribution et un responsable clair pour la rotation des secrets. Préférez une fiabilité sans faille à des démonstrations brillantes mais ponctuelles.
Note de batch pour 0b5d2687b03c : gardez les clés du fournisseur hors du répertoire de code, fixez un plafond pour les jetons par session, et stockez les transcriptions à côté des fichiers d’évaluation afin que les remplacements ultérieurs de modèles restent comparables.
Pour l’étape 0 des notes de renforcement de sécurité, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.
Détail de renforcement 0/810 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez s’il convient de conserver la modification en vous basant sur un ensemble fixe de critères plutôt que sur des observations subjectives.
Lors de la réalisation de la première étape des notes de renforcement, notez d’abord les éléments essentiels : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une responsabilité précise plutôt qu’un processus embrouillé.
Détail de renforcement 1/810 : mesurez le temps d’exécution, la catégorie de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de critères prédéfinis plutôt que sur des observations subjectives.
La note de renforcement 2 fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des surprises financières lorsque le processus passe de l’environnement de démonstration à des environnements partagés.
Détail de renforcement 2/810 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.
Pour la phase 3 de la note de renforcement, définir les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documenter ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’une mise en forme ultérieure.
Détail de renforcement 3/810 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.
Lors de la réalisation de l’étape 4 des notes de renforcement, notez d’abord les conditions du contrat : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminaisons partielles silencieuses.
Détail de renforcement 4/810 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.
La note de renforcement 5 fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.
Détail de renforcement 5/810 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.
Pour l’étape 6 du renforcement, définir les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférer des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.
Détail de renforcement 6/810 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.
Lors de l’étape 7 des notes de renforcement de sécurité, notez d’abord les éléments essentiels : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code.
Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés.
Détail 7/810 du renforcement de sécurité : mesurez le temps d’exécution, la catégorie de l’erreur et la consommation de jetons pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de critères prédéfinis plutôt que sur des observations subjectives.
La note 8 de renforcement de sécurité fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal de fonctionnement, un cas d’échec et les instructions pour le retrait avant d’élargir la portée du projet.
Dokumentez ensemble le parcours normal et celui de récupération. Les tentatives de répétition, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations apportées ultérieurement.
Détail de renforcement 8/810 : mesurer le temps d’exécution du mur, la classe d’erreur et la consommation de jetons pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble fixe de questions plutôt que sur des anecdotes.
Marqueur de réécriture 1 pour 0b5d2687b03c : reformuler les affirmations environnantes en langage d’opérateur, conserver les champs [[CODE_n]] tels quels, et éviter de répéter les phrases sources.
Marqueur de réécriture 2 pour 0b5d2687b03c : reformuler les affirmations environnantes en langage d’opérateur, conserver les champs [[CODE_n]] tels quels, et éviter de répéter les phrases sources.
Marqueur de réécriture 3 pour 0b5d2687b03c : reformuler les affirmations environnantes en langage d’opérateur, conserver les champs [[CODE_n]] tels quels, et éviter de répéter les phrases sources.
Marqueur de réécriture 4 pour 0b5d2687b03c : reformuler les affirmations environnantes en langage d’opérateur, conserver les champs [[CODE_n]] tels quels, et éviter de répéter les phrases sources.
Reformulez le marqueur 5 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases d’origine.
Reformulez le marqueur 6 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases d’origine.
Reformulez le marqueur 7 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases d’origine.
Reformulez le marqueur 8 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases d’origine.
Reformulez le marqueur 9 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases d’origine.
Réécrivez le marqueur 10 pour 0b5d2687b03c : reformulez les affirmations environnantes en langage d’opérateur, conservez les champs [[CODE_n]] tels quels, et évitez de répéter les phrases sources.