# Avvio rapido

Genera ed esegui un agente IA locale, poi aggiungi planner, tool tipizzati e integrazione con il modello.

Source: https://goa.design/it/docs/2-goa-ai/quickstart/

Relative links resolve against the source URL above.


Questa guida ti porta da un modulo vuoto a un agente Goa-AI generato ed eseguibile.
L'esempio generato utilizza il motore in memoria, quindi non è necessario Temporal,
MongoDB, Redis o una chiave API del modello per la prima esecuzione.

Costruirai:

1. Un progetto Goa con un agente, uno strumento digitato e un completamento diretto digitato.
2. Codice di cablaggio dell'agente, del set di strumenti, del completamento e del runtime generato.
3. Un esempio di impalcatura eseguibile con un pianificatore stub che è possibile sostituire con un pianificatore supportato da modello.
4. I primi hook di produzione: sessioni esplicite, esecutori di strumenti generati, streaming e registrazione del modello.

---

## 1. Crea un modulo

```bash
mkdir quickstart && cd quickstart
go mod init example.com/quickstart
go get goa.design/goa-ai@v0.78.8-0.20260915025548-ae0c418b7e77
mkdir design
```

Questa guida usa uno snapshot di sviluppo Goa-AI fissato, non una release stabile. Il modulo Go seleziona la dipendenza Goa compatibile. Esegui il generatore con `go run` per usare quella versione. Usa la versione Go dichiarata dal modulo o una successiva.

---

## 2. Definire l'agente

Crea `design/design.go`:

```go
package design

import (
	. "goa.design/goa/v3/dsl"
	. "goa.design/goa-ai/dsl"
)

var _ = API("orchestrator", func() {})

var AskPayload = Type("AskPayload", func() {
	Attribute("question", String, "User question to answer")
	Example(map[string]any{"question": "What is the capital of Japan?"})
	Required("question")
})

var Answer = Type("Answer", func() {
	Attribute("text", String, "Answer text")
	Example(map[string]any{"text": "Tokyo is the capital of Japan."})
	Required("text")
})

var TaskDraft = Type("TaskDraft", func() {
	Attribute("name", String, "Task name")
	Attribute("goal", String, "Outcome-style goal")
	Example(map[string]any{"name": "Prepare launch checklist", "goal": "Confirm the service is ready to launch."})
	Required("name", "goal")
})

var _ = Service("orchestrator", func() {
	Completion("draft_task", "Produce a task draft directly", func() {
		Return(TaskDraft)
	})

	Agent("chat", "Friendly Q&A assistant", func() {
		Use("helpers", func() {
			Tool("answer", "Answer a simple question", func() {
				Args(AskPayload)
				Return(Answer)
			})
		})
		RunPolicy(func() {
			DefaultCaps(MaxToolCalls(2), MaxRecoveryTurns(1))
			TimeBudget("15s")
		})
	})
})
```

Questa è la fonte della verità. Strumenti e completamenti riutilizzano i normali tipi Goa,
descrizioni, esempi e validazioni. Gli schemi rivolti al modello, i codec tipizzati,
e i contratti di runtime vengono generati da questo progetto.

---

## 3. Genera codice ed esempio

```bash
go mod tidy
go run goa.design/goa/v3/cmd/goa gen example.com/quickstart/design
go run goa.design/goa/v3/cmd/goa example example.com/quickstart/design
go mod tidy
go run ./cmd/orchestrator
```

Forma prevista:

```text
RunID: demo-chat-run
Assistant: Tool helpers.answer returned {"text":"Tokyo is the capital of Japan."}
Completion draft_task: &{Name:Prepare launch checklist Goal:Confirm the service is ready to launch.}
Completion delta draft_task: {"goal":"Confirm the ser
Completion stream draft_task: &{Name:Prepare launch checklist Goal:Confirm the service is ready to launch.}
```

La riga `Completion delta` è un prefisso JSON trasmesso in streaming. Il punto
esatto in cui si interrompe può variare, ma il valore finale trasmesso è
completo e corrisponde all'esempio dichiarato.

`goa gen` crea i contratti generati. `goa example` crea proprietà dell'applicazione
impalcatura:

- `gen/`: codice generato. Non modificare questa directory manualmente.
- `cmd/orchestrator/main.go`: punto di ingresso di esempio eseguibile.
- `internal/agents/orchestrator/bootstrap/bootstrap.go`: costruzione del runtime e registrazione dell'agente.
- `internal/agents/chat/planner/planner.go`: pianificatore stub da sostituire.
- `gen/orchestrator/completions/`: helper digitati per il completamento diretto.

Rigenerare dopo le modifiche DSL. Esegui nuovamente `goa example` quando vuoi l'impalcatura
aggiornamenti, quindi mantenere le modifiche dell'applicazione in `cmd/` e `internal/`.

---

## 4. Comprendere il ciclo di runtime

**Il ciclo di pianificazione/esecuzione:**

1. `PlanStart` accede ai messaggi utente iniziali tramite `PrepareMessages` se ne ha bisogno.
2. Il pianificatore restituisce un `FinalResponse`, chiamate strumento o una richiesta di attesa.
3. Il runtime convalida ed esegue le chiamate agli strumenti ammessi utilizzando le specifiche generate e gli esecutori registrati.
4. `PlanResume` riceve gli output dello strumento visibili dal pianificatore.
5. Il ciclo si ripete finché il pianificatore non restituisce una risposta finale, un risultato dello strumento terminale o il runtime non impone limiti/budget di tempo.

Quando limiti o deadline forzano la finalizzazione, un planner può comunque
chiudere il run restituendo strumenti terminali di bookkeeping. Il runtime
esegue solo strumenti `TerminalRun()` in quel percorso (`TerminalRun()` implica
bookkeeping) e richiede che abbiano successo prima di considerare chiuso il run.

L'esempio generato inizia con uno stub planner in modo che questo flusso sia visibile prima
colleghi un modello. Un vero pianificatore segue lo stesso contratto; delega semplicemente
la decisione a un cliente modello.

---

## 5. Chiama l'agente dal codice

I pacchetti di agenti generati espongono client tipizzati. Le esecuzioni a sessione richiedono un
sessione esplicita; le esecuzioni one-shot sono intenzionalmente senza sessioni.

```go
store := storageinmem.New()
if _, err := store.CreateSession(ctx, "session-1", time.Now().UTC()); err != nil {
	log.Fatal(err)
}

rt, cleanup, err := bootstrap.New(ctx, store)
if err != nil {
	log.Fatal(err)
}
defer cleanup()

client := chat.NewClient(rt)
out, err := client.Run(ctx, "session-1", []*model.Message{{
	Role:  model.ConversationRoleUser,
	Parts: []model.Part{model.TextPart{Text: "Hello"}},
}})
if err != nil {
	log.Fatal(err)
}
fmt.Println(out.RunID)

out, err = client.OneShotRun(ctx, []*model.Message{{
	Role:  model.ConversationRoleUser,
	Parts: []model.Part{model.TextPart{Text: "Summarize this document"}},
}})
```

Utilizzare `Run` o `Start` per il lavoro conversazionale/sessionale. Utilizzare `OneShotRun` o
`StartOneShot` per processi di richiesta/risposta che dovrebbero essere osservabili da `RunID`
ma non dovrebbe appartenere a una sessione.


Lo scaffold locale generato accetta uno `storage.Store` e il comando di esempio usa `runtime/agent/storage/inmem`. In produzione, l’applicazione passa un adattatore per il servizio proprietario del database del runtime. Quel servizio, non un worker dell’agente, crea e termina le sessioni.

---

## 6. Implementa un esecutore di strumenti

I package generati includono `RegisterUsedToolsets` per i toolset locali. Gli
executor ricevono metadati espliciti e restituiscono un risultato posseduto dal
runtime. Ogni package delle specifiche espone inoltre un descrittore tipizzato
per strumento, qui `helpers.AnswerTool`, che associa ID, codec del payload e
codec del risultato senza ripetere manualmente il collegamento:

```go
type HelpersExecutor struct{}

func (e *HelpersExecutor) Execute(
	ctx context.Context,
	meta *runtime.ToolCallMeta,
	call *runtime.ToolCall,
) (*runtime.ToolExecutionResult, error) {
	switch call.Name {
	case helpers.Answer:
		args, err := helpers.AnswerTool().Payload.FromJSON(call.Payload)
		if err != nil {
			return nil, fmt.Errorf("decode admitted %s payload: %w", call.Name, err)
		}
		return runtime.Executed(&planner.ToolResult{
			Name:   call.Name,
			Result: &helpers.AnswerResult{Text: "Answer: " + args.Question},
		}), nil
	default:
		return runtime.Executed(&planner.ToolResult{
			Name: call.Name,
			Failure: &planner.ToolFailure{
				Kind:     planner.FailureInvalidCall,
				Error:    planner.NewToolError("unknown tool"),
				Recovery: planner.RecoveryDirective{Action: planner.RecoveryReplan},
			},
		}), nil
	}
}

if err := chat.RegisterUsedToolsets(ctx, rt, chat.WithHelpersExecutor(&HelpersExecutor{})); err != nil {
	log.Fatal(err)
}
```

Il runtime convalida il payload JSON con i codec generati prima dell'esecuzione,
codifica i risultati positivi con i codec dei risultati generati, registra l'esecuzione canonica
eventi e passa gli output visibili dal pianificatore a `PlanResume`.

---

## 7. Connetti un modello

Registra i clienti del fornitore con il runtime, quindi accedi ad essi dai pianificatori tramite ID.
Per i pianificatori di streaming, preferisci `PlannerModelClient`; possiede assistente/pensiero
e l'emissione di eventi di utilizzo. Questo esempio usa OpenAI; vedi
[Runtime → Integrazione LLM](./runtime/#integrazione-llm) per gli equivalenti
AWS Bedrock e Google Vertex AI (Gemini e Claude-on-Vertex).

```go
modelClient, err := rt.NewOpenAIModelClient(runtime.OpenAIConfig{
	APIKey:       os.Getenv("OPENAI_API_KEY"),
	DefaultModel: "gpt-5-mini",
	HighModel:    "gpt-5",
	SmallModel:   "gpt-5-nano",
})
if err != nil {
	log.Fatal(err)
}
if err := rt.RegisterModel("default", modelClient); err != nil {
	log.Fatal(err)
}
```

Schizzo del pianificatore:

```go
func (p *Planner) PlanStart(ctx context.Context, in *planner.PlanInput) (*planner.PlanResult, error) {
	messages, err := in.PrepareMessages()
	if err != nil {
		return nil, err
	}
	mc, ok := in.Agent.PlannerModelClient("default")
	if !ok {
		return nil, errors.New("model client default is not registered")
	}

	summary, err := mc.Stream(ctx, &model.Request{
		Messages: messages,
		Tools:    in.Agent.AdvertisedToolDefinitions(),
		Stream:   true,
	})
	if err != nil {
		return nil, err
	}
	if len(summary.ToolCalls) > 0 {
		return &planner.PlanResult{ToolCalls: summary.ToolCalls}, nil
	}
	final := summary.FinalResponse()
	if final == nil {
		return nil, errors.New("model stream ended without a canonical response")
	}
	return &planner.PlanResult{
		FinalResponse: final,
		Streamed: true,
	}, nil
}
```

Chiama `PrepareMessages` prima di leggere o trasformare la cronologia e
restituisci ogni errore. Una decisione basata solo sullo stato dell'esecuzione
o sui risultati tipizzati degli strumenti non deve preparare i messaggi.
Vedi il [contratto di preparazione](../runtime/#preparing-conversation-messages).

Utilizza `in.Agent.ModelClient("default")` quando hai bisogno di controllo e associazione del flusso non elaborato
con `planner.ConsumeStream`. Scegli un proprietario dello stream per turno del pianificatore.

---

## 8. Aggiungi streaming

Goa-AI emette eventi di flusso digitati per il testo dell'assistente, l'avvio/fine dello strumento, il flusso di lavoro
stato, attesa, utilizzo e collegamenti di esecuzione figlio. Cablare qualsiasi `stream.Sink`:

```go
type ConsoleSink struct{}

func (s *ConsoleSink) Send(ctx context.Context, event stream.Event) error {
	switch e := event.(type) {
	case stream.AssistantReply:
		fmt.Print(e.Data.Text)
	case stream.ToolStart:
		fmt.Printf("tool_start: %s\n", e.Data.ToolName)
	case stream.ToolEnd:
		fmt.Printf("tool_end: %s\n", e.Data.ToolName)
	case stream.Workflow:
		fmt.Printf("workflow: %s\n", e.Data.Phase)
	}
	return nil
}

func (s *ConsoleSink) Close(ctx context.Context) error { return nil }

rt := runtime.New(runtimeStore, runtime.WithStream(&ConsoleSink{}))
```

Per le UI di produzione, pubblica su Pulse e iscriviti al flusso della sessione
(`session/<session_id>`). Chiudi la connessione utente quando osservi
`run_stream_end` per la corsa attiva.

---

## 9. Utilizza i completamenti diretti digitati

`Completion(...)` è per l'output dell'assistente strutturato che non è una chiamata allo strumento.
Gli helper generati richiedono l'output strutturato imposto dal provider e lo decodificano
codec generati:

```go
resp, err := completions.CompleteDraftTask(ctx, modelClient, &model.Request{
	Messages: []*model.Message{{
		Role:  model.ConversationRoleUser,
		Parts: []model.Part{model.TextPart{Text: "Draft a task for launch readiness."}},
	}},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(resp.Value.Name)
```

I nomi di completamento fanno parte del contratto di output strutturato: 1-64 ASCII
caratteri, lettere/cifre/`_`/`-`, che iniziano con una lettera o una cifra. Streaming
gli aiutanti di completamento espongono i blocchi di anteprima `completion_delta` e decodificano solo i file
pezzo canonico finale `completion`.

---

## 10. Comporre agenti

Gli agenti possono esportare set di strumenti utilizzati da altri agenti. Gli agenti nidificati vengono eseguiti come secondari
flussi di lavoro con il proprio `RunID` e i flussi emettono `child_run_linked` in modo che le interfacce utente possano
eseguire il rendering degli alberi.

```go
Agent("researcher", "Research specialist", func() {
	Export("research", func() {
		Tool("deep_search", "Perform deep research", func() {
			Args(ResearchRequest)
			Return(ResearchReport)
		})
	})
})

Agent("coordinator", "Delegates specialist work", func() {
	Use(AgentToolset("orchestrator", "researcher", "research"))
})
```

Ogni agente mantiene il proprio pianificatore, gli strumenti e la policy. L’archivio del runtime fornito dall’applicazione registra ogni esecuzione radice e figlia, incluso il collegamento al padre. Il padre riceve un normale risultato dello strumento con un `RunLink` all’esecuzione figlia.

---

## Cosa hai costruito

- Un agente incentrato sulla progettazione con strumenti convalidati dallo schema.
- Codec di payload/risultati generati e schemi JSON rivolti al modello.
- Un contratto tipizzato a completamento diretto.
- Un client runtime generato con esecuzione in sessione e one-shot.
- Un percorso verso la pianificazione supportata da modelli, le interfacce utente in streaming e la composizione degli agenti.

Per la produzione, aggiungi il motore Temporal per la durabilità, un unico archivio del runtime di proprietà dell’applicazione, un archivio di memoria di proprietà del prodotto quando necessario, Pulse per lo streaming distribuito e middleware del modello per i limiti di velocità del provider. Il design di Goa rimane la fonte della verità.

---

## Passaggi successivi

| Guida | Cosa imparerai ||-------|-------------------|
| [DSL Reference](dsl-reference/) | Tutte le funzioni DSL: policy, MCP, registri || [Runtime](runtime/) | Pianifica/esegui loop, motori, archivi di memoria || [Toolsets](../toolsets/) | Strumenti supportati da servizi, trasformazioni, esecutori || [Agent Composition](agent-composition/) | Approfondimento sui modelli di agente come strumento || [Production](production/) | Configurazione temporale, streaming alle interfacce utente, limitazione della velocità |


Vedi [Ricerca degli strumenti e cataloghi dinamici](../tool-search/) per risoluzione attuale, contratti generati, provider e migrazione.

