Alloovium

Cas d'usage

Rapports de projet automatisés

Planifiez des rapports de statut hebdomadaires générés en interrogeant votre vault de documents avec l'API chat, livrés sur Slack ou par email.

Ce que vous construirez

Un script de rapport qui interroge un projet Alloovium avec un ensemble de questions prédéfinies, assemble les réponses dans un rapport formaté et le livre à un canal Slack ou une adresse email selon un planning. Aucune infrastructure au-delà d'un processus planifié (cron, GitHub Actions ou un ordonnanceur cloud) n'est requise.

Difficulté et temps de construction

C'est un cas d'usage Débutant. Attendez-vous à deux à quatre heures depuis zéro jusqu'à un rapport hebdomadaire fonctionnel. Scopes requis : scopes.

Prérequis

  • Une clé API Alloovium avec les scopes scopes.
  • Python 3.11+ avec dep installé.
  • Un projet Alloovium existant avec des documents traités — au minimum un contrat et un rapport d'avancement.
  • Une URL de webhook entrant Slack, ou des identifiants SMTP, pour la livraison.

Étape 1 — Configurer un vault de projet

Si vous n'avez pas encore de projet, créez-en un et uploadez les documents source. Le rapport tirera ses réponses de ces documents, donc uploadez le contrat, les rapports d'avancement, les comptes rendus de réunion et tout autre document contenant des informations de statut.

Référez-vous à Intelligence contractuelle — Étape 1 pour le flux d'upload complet. Pour le cas d'usage de rapport, uploader des documents via l'interface web Alloovium convient — il n'est pas nécessaire d'automatiser l'upload pour ce guide.

bash
# Quick check — list your projects and find the one to report on curl -sS "https://api.alloovium.com/api/v1/projects?limit=20" \ -H "Authorization: Bearer $ALLOOVIUM_API_KEY" | python3 -m json.tool

Étape 2 — Rédiger un template de rapport

Définissez les questions qui composent votre rapport comme un template structuré. Un template est juste une liste d'objets question avec un nom de section et un prompt. Cela facilite la maintenance et le versionnage de la structure du rapport indépendamment du code de livraison.

python
from dataclasses import dataclass @dataclass class ReportQuestion: section: str question: str format_hint: str = "" # optional hint to shape the answer WEEKLY_REPORT_TEMPLATE: list[ReportQuestion] = [ ReportQuestion( section="Progress Summary", question="Summarise the overall project progress this week based on the latest reports.", format_hint="2-3 sentences.", ), ReportQuestion( section="Milestones", question=( "Which project milestones have been achieved and which are at risk? " "Include any completion dates mentioned." ), format_hint="Bullet list.", ), ReportQuestion( section="Open RFIs", question=( "List any open RFIs or requests for information from the current documents. " "Include the RFI number and subject if available." ), format_hint="Bullet list, or 'None outstanding' if clear.", ), ReportQuestion( section="Payment Status", question=( "What is the current payment claim status? " "Include the claim number and amount if stated." ), format_hint="1-2 sentences.", ), ReportQuestion( section="Key Risks", question="What are the top three risks to programme or cost mentioned in the documents?", format_hint="Numbered list of three items.", ), ReportQuestion( section="Actions Required", question=( "List any outstanding actions or decisions required from the principal or contractor " "mentioned in the documents." ), format_hint="Bullet list, or 'None identified' if clear.", ), ]

Étape 3 — Appeler l'API chat avec chaque question

Créez une seule conversation par exécution de rapport et envoyez chaque question en séquence. Utiliser une conversation garde la fenêtre de contexte cohérente — le LLM a accès à toutes les réponses précédentes lors de la réponse aux questions ultérieures, ce qui améliore la cohérence.

python
import httpx import json BASE_URL = "https://api.alloovium.com/api/v1" API_KEY = "ak_live_YOUR_KEY_HERE" HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"} async def ask_and_collect( client: httpx.AsyncClient, conversation_id: str, question: str, ) -> str: """Stream a single chat response and return the full answer.""" full: list[str] = [] async with client.stream( "POST", f"{BASE_URL}/conversations/{conversation_id}/messages", json={"content": question}, headers={**HEADERS, "Accept": "text/event-stream"}, timeout=120.0, ) as stream: async for line in stream.aiter_lines(): if not line.startswith("data:"): continue payload = line[5:].strip() if payload in ("", "[DONE]"): continue try: event = json.loads(payload) except json.JSONDecodeError: continue if event.get("type") == "token": full.append(event.get("token", "")) elif event.get("type") == "complete": return event.get("answer", "".join(full)) return "".join(full) async def generate_report_answers( project_id: str, template: list[ReportQuestion], ) -> list[tuple[ReportQuestion, str]]: """Run all questions and return (question, answer) pairs.""" results: list[tuple[ReportQuestion, str]] = [] async with httpx.AsyncClient(timeout=180.0) as client: conv_resp = await client.post( f"{BASE_URL}/conversations", json={"project_id": project_id, "title": "Weekly report"}, headers=HEADERS, ) conv_resp.raise_for_status() conv_id = conv_resp.json()["id"] for q in template: prompt = q.question if q.format_hint: prompt += f"\n\nFormat: {q.format_hint}" answer = await ask_and_collect(client, conv_id, prompt) results.append((q, answer)) return results

Étape 4 — Assembler le rapport

Prenez la liste des questions répondues et formatez-les en un rapport lisible. L'exemple ci-dessous produit à la fois une version en texte brut (pour l'email) et une version markdown Slack.

python
from datetime import date def assemble_report( project_name: str, answers: list[tuple[ReportQuestion, str]], ) -> tuple[str, str]: """Return (plain_text, slack_markdown) versions of the report.""" today = date.today().strftime("%d %B %Y") header = f"Weekly Project Report — {project_name} — {today}" plain_lines = [header, "=" * len(header), ""] slack_lines = [f"*{header}*", ""] for q, answer in answers: plain_lines.append(f"{q.section}") plain_lines.append("-" * len(q.section)) plain_lines.append(answer.strip()) plain_lines.append("") slack_lines.append(f"*{q.section}*") slack_lines.append(answer.strip()) slack_lines.append("") return "\n".join(plain_lines), "\n".join(slack_lines)

Étape 5 — Livrer via Slack ou email

Livrer sur Slack

Postez sur un webhook entrant Slack. Créez un webhook dans votre espace de travail Slack sous path et définissez l'URL dans votre environnement.

python
import os async def deliver_to_slack(slack_markdown: str) -> None: """Post the report to a Slack channel via incoming webhook.""" webhook_url = os.environ["SLACK_WEBHOOK_URL"] async with httpx.AsyncClient() as client: resp = await client.post( webhook_url, json={"text": slack_markdown}, headers={"Content-Type": "application/json"}, ) resp.raise_for_status()

Livrer par email

Utilisez le module intégré Python lib pour envoyer le rapport en texte brut. Pour les emails formatés en HTML, enveloppez les sections dans du HTML simple à la place.

python
import smtplib import os from email.message import EmailMessage def deliver_by_email( plain_text: str, subject: str, to_addresses: list[str], ) -> None: """Send the plain-text report via SMTP.""" msg = EmailMessage() msg["Subject"] = subject msg["From"] = os.environ["SMTP_FROM"] msg["To"] = ", ".join(to_addresses) msg.set_content(plain_text) with smtplib.SMTP_SSL( os.environ["SMTP_HOST"], int(os.environ.get("SMTP_PORT", "465")), ) as smtp: smtp.login(os.environ["SMTP_USER"], os.environ["SMTP_PASSWORD"]) smtp.send_message(msg)

Étape 6 — Configuration du cron job

Assemblez tout dans un script de point d'entrée unique et planifiez-le avec cron, GitHub Actions ou un ordonnanceur cloud.

Script de point d'entrée

python
# weekly_report.py import asyncio import os PROJECT_ID = os.environ["ALLOOVIUM_PROJECT_ID"] PROJECT_NAME = os.environ.get("PROJECT_NAME", "Project") REPORT_EMAIL_TO = os.environ.get("REPORT_EMAIL_TO", "").split(",") async def main() -> None: answers = await generate_report_answers(PROJECT_ID, WEEKLY_REPORT_TEMPLATE) plain_text, slack_md = assemble_report(PROJECT_NAME, answers) if os.environ.get("SLACK_WEBHOOK_URL"): await deliver_to_slack(slack_md) if REPORT_EMAIL_TO and REPORT_EMAIL_TO[0]: from datetime import date subject = f"Weekly Report: {PROJECT_NAME} — {date.today():%d %b %Y}" deliver_by_email(plain_text, subject, REPORT_EMAIL_TO) print("Report delivered successfully.") if __name__ == "__main__": asyncio.run(main())

Cron Linux/macOS (tous les lundis à 07h00)

bash
# Add to crontab with: crontab -e 0 7 * * 1 cd /opt/reports && /usr/bin/python3 weekly_report.py >> /var/log/weekly_report.log 2>&1

GitHub Actions (tous les lundis à 07h00 UTC)

yaml
# .github/workflows/weekly-report.yml name: Weekly Project Report on: schedule: - cron: '0 7 * * 1' workflow_dispatch: # allow manual runs jobs: report: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - run: pip install httpx - run: python weekly_report.py env: ALLOOVIUM_API_KEY: ${{ secrets.ALLOOVIUM_API_KEY }} ALLOOVIUM_PROJECT_ID: ${{ secrets.ALLOOVIUM_PROJECT_ID }} PROJECT_NAME: "City Road Upgrade" SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} REPORT_EMAIL_TO: ${{ secrets.REPORT_EMAIL_TO }} SMTP_HOST: smtp.example.com SMTP_FROM: reports@example.com SMTP_USER: ${{ secrets.SMTP_USER }} SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}

Gardez les secrets hors du contrôle de source

Stockez key, les identifiants SMTP et les URLs de webhook Slack comme secrets de dépôt ou dans un gestionnaire de secrets. Ne les committez jamais dans le dépôt.

Aller plus loin

  • Pipeline d'intelligence contractuelle — incluez l'extraction des dates d'échéance des jalons dans votre rapport hebdomadaire.
  • Référence API Chat — format de citation, portée des conversations et schéma complet des événements SSE.
  • Ajoutez des rapports multi-projets en bouclant sur une liste d'IDs de projet et en livrant un digest combiné avec une conversation par projet.