Last chance! 50% off DataCamp Premium
Sale ends in
LangChain è un framework open-source per creare applicazioni IA agentiche che combinano LLM e strumenti. La versione 1.0 segna il passaggio a un core di livello production con un’API semplificata, confini più chiari e una gestione standardizzata dei messaggi tra provider.
In questo tutorial costruiremo uno Streamlit Auto Meeting Recap Assistant che prende appunti grezzi, produce un riepilogo conciso con elementi d’azione e, dopo l’approvazione umana, aggiunge il riepilogo a un Google Doc. Lungo il percorso vedrai in azione il nuovo flusso create_agent, i content block standard e il namespace più snello della v1.
LangChain v1 è una release ristrutturata e orientata alla produzione, focalizzata su una superficie piccola e stabile per costruire agenti. Introduce:
create_agent come modo standard per avviare agenti (più pulito rispetto ai vecchi agenti predefiniti).
Contenuti standardizzati tramite content_blocks così i messaggi sono coerenti tra provider.
Output strutturato e middleware per risposte tipizzate e azioni sicure, approvate dall’uomo.
Il pacchetto langchain si concentra sui mattoni per costruire agenti, mentre i moduli legacy si spostano su langchain-classic, facilitando reperibilità e upgrade.
Rispetto alla v0.x, la v1 riduce il carico cognitivo e le particolarità dei provider standardizzando contenuto dei messaggi e orchestrazione degli agenti. Ottieni lo stesso modello mentale sia che usi OpenAI, Anthropic o altri.
La versione originale LangChain v0.x offriva molti pattern per costruire agenti, tuttavia la v1 li consolida in un percorso chiaro usando il blocco create_agent così i tuoi agenti sono più facili da comprendere, testare e distribuire. Offre strumenti più puliti, debug più semplice tramite contenuto standardizzato e un loop dell’agente prevedibile.
Ecco alcuni miglioramenti tecnici chiave inclusi in questa release:
Workflow create_agent: questo loop d’agente pronto per la produzione consente al modello di decidere quando chiamare strumenti e quando terminare. Non dobbiamo più destreggiarci tra più pattern, il che semplifica la personalizzazione.
Content block standard: un modo agnostico rispetto al provider per leggere i messaggi con tracce di ragionamento, chiamate a tool, citazioni e persino elementi multimodali, tutto tramite un’API unificata content_blocks. Strumentazione e log appaiono uguali tra provider.
Output strutturato: l’ultima release include un supporto di prima classe per schemi tipizzati, così puoi fare affidamento su JSON prevedibile senza regex fragili. Questo riduce errori di parsing e chiamate extra.
Middleware: si agganciano al loop dell’agente per approvare azioni rischiose, riassumere la cronologia quando è lunga e redigere PII prima delle chiamate.
Namespace semplificato: il pacchetto langchain ora si concentra sui componenti essenziali per gli agenti, mentre LangGraph fornisce sotto il cofano funzionalità di affidabilità come persistenza e time-travel.
In questa sezione implementeremo un’app Streamlit che automatizza i riepiloghi delle riunioni end-to-end. L’app accetta appunti grezzi della riunione con metadati opzionali (titolo, data e partecipanti). Genera quindi un RecapDoc tipizzato, includendo titolo, sintesi, decisioni ed elementi d’azione, in un output strutturato. Con un clic, il riepilogo approvato (preceduto dalla data della riunione) viene aggiunto a un Google Doc designato.
Prima di costruire l’Auto Meeting Recap Assistant, ci serve un toolkit minimo per la UI, l’LLM, la validazione tipizzata e l’accesso a Google Docs. I comandi seguenti installano tutto il necessario e impostano la chiave del modello in modo che la tua app possa comunicare con il provider.
pip install -U streamlit langchain langchain-openai pydantic python-dotenv
pip install -U google-api-python-client google-auth google-auth-oauthlib google-auth-httplib2
export OPENAI_API_KEY=...
Ecco a cosa servono i prerequisiti:
Streamlit: esegue l’app web in locale con una UI semplice e pythonica.
langchain e langchain-openai: forniscono la superficie semplificata di LangChain v1 (init_chat_model, output strutturato) e il bridge al provider OpenAI.
pydantic: definisce lo schema rigoroso RecapDoc così il modello restituisce campi tipizzati.
Python-dotenv: carica le variabili d’ambiente da un file .env durante lo sviluppo.
google-auth: gestisce OAuth e la Google Docs API così possiamo aggiungere il riepilogo a un documento.
OPENAI_API_KEY: la chiave API di OpenAI autentica le chiamate al LLM; impostala come variabile d’ambiente (o usa un secrets manager).
Nota: se usi un altro provider (Anthropic, Google o un modello locale), installa l’integrazione langchain-* corrispondente e imposta la chiave API di quel provider.
Con queste dipendenze installate e OPENAI_API_KEY impostata, l’ambiente è pronto. Nei prossimi passi configureremo la Google Docs API tramite Google Cloud.
Per salvare ogni riepilogo in un documento vivo, l’app ha bisogno del permesso di scrivere su Google Docs. Questo passo configura l’accesso end-to-end. Per prima cosa:
Ottieni un file OAuth client (credentials.json) da Google Cloud
Identifica l’ID del Google Doc a cui vuoi aggiungere contenuti
Infine, lascia che l’app crei un token.json riutilizzabile al primo avvio dopo che approvi l’accesso nel browser.
Una volta predisposti questi tre elementi, l’app può aggiungere i riepiloghi al Google Doc scelto con un clic. Puoi revocare o rinnovare l’accesso in qualsiasi momento eliminando token.json ed eseguendo di nuovo l’app.
Il file credentials.json è la chiave della tua app per la Google Docs API. Lo generi una volta nella Google Cloud Console come OAuth client per Desktop App. Per iniziare:

Ora che il progetto è stato creato, abilitiamo l’API necessaria per scrivere in Google Docs.

Una volta abilitata l’API, dobbiamo impostare l’autenticazione per la nostra email preferita.

Il passo finale è generare e scaricare il file credentials.json. Per farlo:
Vai su APIs & Services e clicca su Credentials.
Clicca su + CREATE CREDENTIALS, verrai indirizzato a OAuth client ID.


Possono essere necessari fino a 5 minuti per generare client ID e secret, insieme a un file JSON scaricabile.
Salva il file JSON come credentials.json nella stessa cartella del tuo app.py (oppure indica il suo percorso nelle impostazioni dell’app).
Ora disponi di un OAuth client Desktop che l’app può usare per avviare l’accesso tramite browser e richiedere il permesso di accedere a Google Docs per tuo conto.
L’app deve sapere a quale documento aggiungere contenuti. Il documento è identificato da un ID univoco incorporato nell’URL. Per questo passo,
Apri il Google Doc di destinazione in un browser.
Copia la stringa nell’URL tra /d/ e /edit, per esempio: https://docs.google.com/document/d/<THIS_IS_THE_ID>/edit
Incolla questo Doc ID nel campo “Google Doc ID” dell’app (o nella variabile di configurazione appropriata).
Con l’ID corretto, l’app scriverà i riepiloghi esattamente nel documento selezionato.
Al primo inserimento, l’app ha bisogno della tua approvazione esplicita per accedere al documento. Dopo l’approvazione, Google restituisce un token che l’app archivia in locale come token.json. Ecco come viene generato questo file:
Esegui l’app e clicca Append to Google Doc.
Si apre una finestra del browser che ti chiede di accedere con uno degli utenti di test aggiunti in precedenza e di approvare l’ambito.
Dopo la conferma, l’app scrive token.json accanto al tuo codice. Questo file contiene token di accesso/refresh riutilizzabili.
Nota: se devi cambiare account o ripetere il consenso, elimina semplicemente token.json e clicca di nuovo Append to Google Doc.
Prima di scrivere la logica, importiamo i moduli che alimentano la UI, i contratti di dati tipizzati, le chiamate al modello e l’integrazione con Google Docs. Raggruppare gli import per scopo mantiene il codice leggibile e rende ovvio quale livello verificare quando qualcosa non funziona.
import os
import io
import json
from datetime import date
from typing import List, Optional
import streamlit as st
from pydantic import BaseModel, Field
from dotenv import load_dotenv
# ---- LangChain v1 surface ----
from langchain.agents import create_agent
from langchain.messages import SystemMessage, HumanMessage
from langchain.chat_models import init_chat_model
# ---- Google Docs API ----
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
Per questo passo importiamo solo ciò che serve all’app, ossia utility standard come os, io, json, date e hint di typing per percorsi, buffer e serializzazione leggera. Poi importiamo Streamlit per la UI, Pydantic per gli schemi tipizzati RecapDoc, e dotenv per caricare le variabili d’ambiente. La superficie di LangChain v1 init_chat_model e create_agent per inizializzare un modello di chat agnostico rispetto al provider e strutturare i prompt e, infine, i componenti della Google Docs API che gestiscono OAuth e ci danno un client autenticato per aggiungere riepiloghi al documento scelto.
Per rendere la risposta del modello affidabile e facile da usare, definiamo un contratto tipizzato per il riepilogo. Usare modelli Pydantic assicura che l’LLM restituisca campi prevedibili che la nostra UI può renderizzare, l’API può archiviare e le automazioni possono validare.
class ActionItem(BaseModel):
owner: str = Field(..., description="Person responsible")
task: str = Field(..., description="Short, specific task")
due_date: str = Field(..., description="ISO date (YYYY-MM-DD) or natural language like 'next Friday'")
class RecapDoc(BaseModel):
title: str
date: str
attendees: List[str]
summary: str
decisions: List[str]
action_items: List[ActionItem]
Ecco i componenti chiave del blocco di codice precedente:
Classe ActionItem: cattura l’essenziale di un’attività di follow-up come owner, task e due_date con descrizioni brevi ed esplicite così il modello le compila pulitamente. Consentire date ISO o frasi naturali mantiene l’input flessibile pur restando analizzabile a valle.
Classe RecapDoc: rappresenta l’intero riepilogo come un singolo oggetto, inclusi title, date, attendees, un summary conciso, decisions esplicite e un elenco di action_items.
Con LangChain v1, puoi richiedere direttamente questo schema tramite with_structured_output(RecapDoc), riducendo il parsing fragile delle stringhe, migliorando la validazione e consentendo di fallire rapidamente quando mancano campi.
Il system prompt stabilisce le regole di base per il tuo assistente. Dice al modello esattamente cosa produrre, cosa evitare e come strutturare l’output affinché i passaggi successivi funzionino in modo affidabile ogni volta.
SYSTEM_PROMPT = """You are a precise assistant that produces concise, high-signal meeting recaps.
Return a structured RecapDoc with:
- title, date, attendees
- a brief summary (3–6 sentences)
- explicit decisions (bullet-style)
- action_items (each has owner, task, due_date)
Rules:
- Only include info supported by the notes or explicit user inputs.
- Keep action items specific with clear owners and due dates.
- If something is unknown, say "Unknown" rather than inventing details.
"""
Il system prompt rende l’assistente preciso e conciso, puntando a riepiloghi ad alto valore informativo invece che trascrizioni. Stampa uno schema netto che include titolo, data, partecipanti, sintesi, decisioni, action_items con regole come sintesi di 3–6 frasi, decisioni puntate ed elementi d’azione con owner, task e scadenza.
Con un system prompt chiaro, il modello produce costantemente riepiloghi strutturati e utilizzabili al primo tentativo.
In questo passo definiamo una funzione helper che stabilisce una connessione affidabile e riutilizzabile a Google Docs così l’app può aggiungere i riepiloghi al documento scelto. Richiede solo l’ambito Docs e gestisce il consenso iniziale, il refresh del token e la costruzione del servizio.
SCOPES = ["https://www.googleapis.com/auth/documents"]
def get_google_docs_service(
credentials_path: Optional[str],
token_path: str = "token.json",
use_secrets: bool = False
):
creds = None
if use_secrets:
try:
if "google_credentials_json" in st.secrets:
with open("credentials_temp.json", "w") as f:
f.write(st.secrets["google_credentials_json"])
credentials_path = "credentials_temp.json"
except Exception:
pass
if os.path.exists(token_path):
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request())
except Exception:
pass
if not creds or not creds.valid:
if not credentials_path or not os.path.exists(credentials_path):
raise RuntimeError(
"Missing Google OAuth credentials. Provide 'credentials.json' "
"or set st.secrets['google_credentials_json']."
)
flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)
creds = flow.run_local_server(port=0)
with open(token_path, "w") as token:
token.write(creds.to_json())
return build("docs", "v1", credentials=creds)
La funzione get_google_docs_service() gestisce OAuth e la creazione del servizio:
Definizione dell’ambito: la funzione usa SCOPES predefiniti per limitare i permessi a Google Docs.
Caricamento credenziali: legge da un credentials.json locale o da st.secrets["google_credentials_json"], il contenitore dei segreti di Streamlit quando use_secrets=True.
Riutilizzo dei token: se esiste token.json, il codice lo carica e, se il token è scaduto, lo aggiorna in silenzio.
Consenso iniziale: se non si trova un token valido, si avvia l’InstalledAppFlow Desktop, che chiede all’utente di autenticarsi nel browser e poi salva token.json per esecuzioni future.
Restituzione del client: infine, costruiamo e restituiamo un client Docs autenticato tramite la funzione build().
Una volta configurato, puoi riutilizzare lo stesso token.json per l’autenticazione o aggiornare automaticamente il token. Per reimpostare l’accesso, elimina token.json ed esegui di nuovo l’app.
Ora trasformiamo un RecapDoc tipizzato in Markdown pulito da aggiungere al Google Doc di destinazione. Una funzione renderizza il riepilogo, mentre l’altra esegue la scrittura autenticata alla fine del documento.
def append_plaintext_to_doc(docs_service, document_id: str, text: str):
doc = docs_service.documents().get(documentId=document_id).execute()
end_index = doc.get("body", {}).get("content", [])[-1]["endIndex"]
requests = [
{
"insertText": {
"location": {"index": end_index - 1},
"text": text + "\n"
}
}
]
return docs_service.documents().batchUpdate(
documentId=document_id,
body={"requests": requests}
).execute()
def recap_to_markdown(recap: RecapDoc) -> str:
lines = [
f"# {recap.title} — {recap.date}",
"",
f"**Attendees:** {', '.join(recap.attendees) if recap.attendees else 'Unknown'}",
"",
"## Summary",
recap.summary.strip(),
"",
"## Decisions",
]
if recap.decisions:
for d in recap.decisions:
lines.append(f"- {d}")
else:
lines.append("- None recorded")
lines.append("")
lines.append("## Action Items")
if recap.action_items:
for ai in recap.action_items:
lines.append(f"- **{ai.owner}** — {ai.task} _(Due: {ai.due_date})_")
else:
lines.append("- None recorded")
return "\n".join(lines)
Le due helper sopra lavorano insieme come segue:
La funzione recap_to_markdown() costruisce un riassunto leggibile. Include un titolo con data, partecipanti, una sintesi concisa ed elementi d’azione con owner, task e scadenza. Passa a “Unknown” o “None recorded” quando mancano campi.
La funzione append_plaintext_to_doc() recupera il documento, trova l’indice finale corrente e invia una singola richiesta insertText tramite documents(). Il testo è aggiunto con un newline finale così le voci successive iniziano su una nuova riga.
Nota: per documenti vuoti, valuta di impostare l’indice di inserimento a 1 se endIndex non è disponibile.
Con il rendering Markdown e l’append a posto, impostiamo il generatore di riepiloghi.
Questa funzione è il cuore dell’app. Prende appunti grezzi e metadati opzionali, chiama un modello di chat e restituisce un RecapDoc tipizzato così tutto a valle resta prevedibile.
def generate_recap(model_name: str, notes: str, title: str, date_str: str, attendees_csv: str) -> RecapDoc:
model = init_chat_model(model=model_name)
structured_llm = model.with_structured_output(RecapDoc)
attendees_hint = [a.strip() for a in attendees_csv.split(",")] if attendees_csv.strip() else []
user_prompt = (
"You will receive meeting notes and metadata.\n\n"
f"Title: {title or 'Unknown'}\n"
f"Date: {date_str or 'Unknown'}\n"
f"Attendees: {attendees_hint if attendees_hint else 'Unknown'}\n\n"
"Notes:\n"
f"{notes.strip()}\n"
)
messages = [
SystemMessage(content=SYSTEM_PROMPT),
HumanMessage(content=user_prompt)
]
try:
recap = structured_llm.invoke(messages)
except Exception as e:
st.error(f"Error generating recap: {e}")
recap = RecapDoc(
title=title or "Unknown",
date=date_str or "Unknown",
attendees=attendees_hint or [],
summary=f"Error generating summary: {str(e)}",
decisions=[],
action_items=[]
)
return recap
Il generatore del riepilogo include tre componenti chiave:
Inizializzazione del modello: la funzione init_chat_model() crea un modello di chat per il provider scelto. Poi chiamiamo with_structured_output() per dire al modello esattamente la forma dell’output. Restituisce un oggetto Python che corrisponde ai campi di RecapDoc.
Assemblaggio del prompt: normalizziamo gli input e inviamo due messaggi, incluso un SystemMessage con regole rigide (il system prompt) e un HumanMessage con titolo, data, partecipanti e appunti grezzi.
Generazione strutturata: la funzione structured_llm.invoke() restituisce un RecapDoc validato, evitando il parsing e riducendo le allucinazioni.
Instradando il tuo modello GPT tramite il wrapper di output strutturato di LangChain, il passo del riepilogo diventa affidabile e pronto per il rendering Markdown senza cambiare la logica a valle.
Questo passo collega l’intera demo in un’app Streamlit a pagina singola. L’utente incolla gli appunti, aggiunge metadati opzionali, genera un riepilogo strutturato, lo visualizza in anteprima come Markdown e lo aggiunge a un Google Doc con un clic.
def main():
load_dotenv()
st.set_page_config(page_title="Meeting Recap Assistant (LangChain v1)", page_icon=" ", layout="wide")
st.markdown("<h1 style='text-align: center;'>Meeting Recap Assistant With LangChain v1</h1>", unsafe_allow_html=True)
model_name = os.getenv("LC_MODEL", "gpt-4o-mini")
document_id = "10G1k8-2JG_phkpjWM3xZEy2wNg5trUO0SJ2WN7kR3po"
cred_mode = "credentials.json file"
credentials_path = os.getenv("GOOGLE_CREDENTIALS_JSON", "credentials.json")
st.subheader(" Add your meeting notes")
colL, colR = st.columns([2, 1])
with colL:
notes = st.text_area(" ", height=300, placeholder="Paste your raw notes here...")
with colR:
title = st.text_input("Meeting Title", value="")
date_str = st.date_input("Meeting Date", value=date.today())
attendees_csv = st.text_input("Attendees (comma-separated)", value="")
if "recap" not in st.session_state:
st.session_state.recap = None
if "markdown_text" not in st.session_state:
st.session_state.markdown_text = None
col1, col2, col3 = st.columns([1, 1, 2])
with col1:
generate_btn = st.button("Generate Recap")
with col2:
append_btn = st.button("Append to Google Doc", disabled=(st.session_state.recap is None))
if generate_btn:
if not notes.strip():
st.error("Please paste some notes.")
st.stop()
try:
recap = generate_recap(
model_name=model_name,
notes=notes,
title=title,
date_str=str(date_str),
attendees_csv=attendees_csv,
)
st.session_state.recap = recap
st.session_state.markdown_text = recap_to_markdown(recap)
st.rerun()
except Exception as e:
st.exception(e)
st.stop()
if append_btn and st.session_state.recap is not None:
try:
use_secrets = (cred_mode == "Streamlit secrets")
service = get_google_docs_service(
credentials_path=credentials_path if cred_mode == "credentials.json file" else None,
use_secrets=use_secrets
)
final_text = f"\n\n===== {st.session_state.recap.title} — {st.session_state.recap.date} =====\n\n" + st.session_state.markdown_text
append_plaintext_to_doc(service, document_id, final_text)
st.success("Recap appended to the Google Doc")
except Exception as e:
st.exception(e)
if st.session_state.recap is not None:
st.markdown("---")
st.markdown(st.session_state.markdown_text)
if __name__ == "__main__":
main()
L’app Streamlit collega un’unica azione “Generate Recap” al modello ed espone il tutto a una UI web locale, usando:
Input (colonne sinistra/destra): il riquadro a sinistra è un ampio st.text_area per gli appunti grezzi. Quello a destra raccoglie titolo, data e partecipanti.
Output (area di anteprima): quando esiste un riepilogo, l’app mostra un divisore e un’anteprima Markdown del riepilogo strutturato per una revisione rapida.
Collegamento dell’interfaccia (azioni): queste azioni costituiscono il cuore dell’app. Includono:
Generate recap: valida gli appunti, chiama la funzione generate_recap(), converte in Markdown e usa st.rerun() per aggiornare.
Append to Google Doc: crea un client autenticato con get_google_docs_service(), aggiunge un’intestazione datata e inserisce nel documento di destinazione.
Stato: st.session_state archivia l’ultimo RecapDoc e markdown_text così la UI resta stabile tra le riesecuzioni.
Impostazione dell’app: infine, load_dotenv() legge le variabili d’ambiente mentre st.set_page_config() imposta il layout e i percorsi delle credenziali provengono dall’ambiente o da valori predefiniti.
Salva come app.py ed esegui il seguente comando nel terminale.
streamlit run app.py
Ora hai uno strumento end-to-end per i riepiloghi delle riunioni che appare così:



LangChain v1 semplifica la creazione di agenti con import più semplici e risultati più affidabili grazie a output strutturati e content block standard. Se provieni dalla v0, gestisci l’upgrade come qualsiasi modifica in produzione con versioni bloccate, refactor degli import e nuove primitive.
Alcune considerazioni da tenere a mente:
LangChain v1 usa create_agent invece dei vecchi agenti predefiniti, facendo leva su content block standard e output strutturato; nota che alcune parti più vecchie sono passate a langchain-classic.
Quando aggiorni, blocca langchain>=1.0 e usa Python 3.10+. Preferisci modelli Pydantic per gli output, aggiungi middleware solo dove serve (approvazioni, redazione PII, riassunti) e mantieni langchain-classic se ti servono ancora retriever legacy.
Infine, invia prima un input piccolo e osserva tracce ed errori mantenendo le dipendenze bloccate.
LangChain v1 offre primitive più pulite per app reali, output strutturati affidabili, content block standard tra provider e un modo unificato per costruire agenti. Di seguito, modi pratici per sfruttarlo, insieme ad alcune difficoltà che potresti incontrare in fase di adozione.
Alcuni casi d’uso ad alto impatto di questa versione includono:
Con casi d’uso ad alto impatto arrivano alcune sfide:
Guardando avanti, possiamo aspettarci persistenza, time-travel e osservabilità continuative (tramite LangGraph e LangSmith), insieme a un supporto più ampio dei provider per content block standard e controlli più chiari per i team di produzione.
LangChain v1 è una base solida per costruire agenti affidabili e manutenibili. Questo Auto Meeting Recap Assistant mostra come create_agent, content block standard e output strutturato si combinino per gestire un flusso reale con approvazione umana nel loop e un passaggio senza attriti a Google Docs. Usa le guide ufficiali di migrazione per modernizzare il codice legacy e inizia in piccolo con un’app tutorial come questa per prendere confidenza.
Se vuoi fare più pratica nella creazione di applicazioni basate sull’IA, ti consiglio il nostro corso Developing Applications with LangChain .
Sono una Google Developers Expert in ML (Gen AI), una Kaggle 3x Expert e una Women Techmakers Ambassador con oltre 3 anni di esperienza nel tech. Ho co-fondato una startup health-tech nel 2020 e sto conseguendo un master in informatica al Georgia Tech, con specializzazione in machine learning.
Impara con DataCamp
Corso
Corso
Corso

blog
Abid Ali Awan
15 min

blog
Abid Ali Awan
10 min
blog
Tim Lu
12 min