Define your tools once. Generate the schemas the model sees, the Go types your implementation uses, and the validation that connects them. Goa-AI runs the agent loop and helps the model correct invalid tool calls with specific feedback and valid examples from your design.
Part of the Goa ecosystem: use Goa for services and Goa-AI for agents. They share the same Go design and generation workflow, so a service method can also become an agent tool.
Quick start · Documentation · What you can build · Releases
- Your schemas stay in sync. A tool bound to a Goa method inherits its input and result types. Regenerate after a design change to update model schemas, typed codecs, and service bindings together. HTTP/OpenAPI and gRPC/protobuf come from that same design when those transports are declared.
- Invalid tool calls get a path to recovery. A missing field or wrong type produces clear correction guidance. Authored examples show the model a valid argument structure, and the runtime can request a replacement response within your recovery budget. Invalid model arguments never reach the tool implementation.
- Coding agents have less contract code to write. Change the design, regenerate, then implement the application behavior. You and your coding agent work from explicit types and a predictable directory structure, with compiler feedback when implementation code no longer matches.
- Runs reference history in its owning store. Preparation publishes bounded history and the exact compiled request before submission. Large inline messages use ordered literal byte parts without changing message or image content; complete literals keep their existing encoding. Lost replies recover the accepted prompt and policy; application rows retain compact references. New turns reference one exact completed run; workflows, planner commands, and checkpoints carry saved positions. Activities reconstruct original messages through bounded store reads. See the runtime store contract for store implementation and the required persisted-format cutover.
- Closed runs retain their original start and outcome. The four engine start operations bind the complete accepted request to its first stored start. A new execution of the same closed request returns the original records and stops before hooks or agent work. Synchronous callbacks use a separate exact command operation. Hosts must adopt all five Store methods and follow the start-result upgrade requirements.
The Responses adapter preserves typed nested stream failures, including transient server-error metadata. Retry owners must still protect already-published output; classification does not replay streams. See the provider stream contract.
Bedrock Fable 5.1 requests with forced tool or any choices fail locally as
model.RequestValidationError before inference or counting. The adapter preserves
the caller's selected model and choice; see the
Claude tool-choice contract.
With Go 1.26.0 or newer, run the checked-in example:
git clone https://github.com/goadesign/goa-ai.git
cd goa-ai/quickstart
go run ./cmd/orchestratorThis checkout uses Goa v3.32.0. Run generation through
go run goa.design/goa/v3/cmd/goa gen <design-package> to use the version
selected by your module.
You'll see a complete tool call and response, followed by typed completion examples:
Assistant: Tool helpers.answer returned {"text":"Tokyo is the capital of Japan."}
Completion draft_task: Prepare launch checklist (Confirm the service is ready to launch.), 3 steps
This is a deterministic demonstration of the real runtime, with an in-memory engine and store. It needs no model API key, Temporal, Redis, or MongoDB. The planner and example executor are application code you replace when connecting a model and your services.
Run its evaluation suite too:
go run ./cmd/chat_quality-evalsFollow the quickstart guide to edit the design, regenerate,
and connect a model. This README describes main; consult the
release notes and
upgrade guide when updating an existing application.
Here is a complete design package. The service method and the agent tool share
Lookup and Product; BindTo connects the tool to the method:
package design
import (
. "goa.design/goa/v3/dsl"
. "goa.design/goa-ai/dsl"
)
var Lookup = Type("Lookup", func() {
Field(1, "sku", String, "Product SKU")
Required("sku")
})
var Product = Type("Product", func() {
Field(1, "name", String, "Product name")
Required("name")
})
var _ = Service("catalog", func() {
Description("Look up products for applications and shopping assistants.")
Method("lookup", func() {
Description("Retrieve a product by its SKU.")
Payload(Lookup)
Result(Product)
HTTP(func() { GET("/products/{sku}") })
GRPC(func() {})
})
Agent("shopper", "Help customers find products", func() {
Use("products", func() {
Tool("lookup", "Look up a product by SKU", func() {
Args(Lookup, func() {
Example(map[string]any{"sku": "SKU-123"})
})
Return(Product)
BindTo("lookup")
})
})
})
})Run goa gen with your design package's import path. For this design, Goa and
Goa-AI generate:
| Consumer | Generated from the same design |
|---|---|
| The model | JSON Schema, field descriptions, and the authored {"sku":"SKU-123"} example |
| Your tool implementation | Typed Go payloads/results, JSON codecs, validation, and service transforms |
| HTTP clients | Server/client code and OpenAPI specifications for GET /products/{sku} |
| gRPC clients | Server/client code and Protocol Buffer definitions with stable field numbers |
| The agent runtime | Tool catalog, registration helpers, agent client, and workflow definitions |
Change a field once and regenerate these artifacts together. Each transport keeps its own representation; for example, protobuf presence and JSON required fields are enforced through the generated transport code. You don't maintain a second, handwritten model schema beside the service contract.
Tools can also have a smaller, purpose-built input or result. Use Args and
Return with generated transforms, and Inject for server-supplied fields that
the model should not fill in. Service bindings and injection
explain those choices.
Suppose the model calls products.lookup with {}. Goa-AI rejects the arguments
and supplies feedback derived from the generated contract, including:
Field "sku" is required. Field description: "Product SKU".
Example illustrates structure; use values and a valid variant appropriate to the request:
{"sku":"SKU-123"}
The runtime schedules a replacement planning turn within MaxRecoveryTurns.
The model can supply the missing SKU, choose another permitted action, or ask
for information. A corrected call is validated again before execution.
When validation rejected a complete response, the replacement also receives every submitted call in order, with its original argument text quoted as untrusted, unexecuted data. Generated instructions remain separate. An eligible typed input rejection with no complete retained response receives only its generated correction. Rejected calls never become accepted conversation or tool-execution evidence. This context is recorded in Temporal activity history; deployments must follow the recovery upgrade and retention contract.
This works for more than missing fields: wrong JSON types, invalid enum values,
and array-length errors can receive field-specific guidance. The complete
example comes from your top-level Example(...) and is included when it fits
the correction message. It teaches structure; the model still chooses values
appropriate to the user's request.
Generated service executors also return structured validation failures, so the runtime can carry correction evidence back to the planner. Your application still owns business rules and authorization. See tool input validation and recovery.
Install the Goa service designer skill in your application project (requires Node.js and npm):
npx skills add goadesign/goa --skill goa-service-designerThe skill guides Goa service design, transport mappings, and regeneration.
For Goa-AI wiring, also give your coding agent the generated
AGENTS_QUICKSTART.md: it names your agents, tools, packages, and registration
functions. Use the Goa-AI guides for runtime
and agent-specific choices.
A useful task to start with:
Add a product lookup tool backed by the catalog service. Update the Goa design, regenerate, implement the service method, and test a valid call and an invalid call. Use AGENTS_QUICKSTART.md for wiring. Do not edit gen/.
goa gen replaces generated contracts. goa example creates missing application
scaffolding without overwriting existing files. Your planner, service logic,
and tests remain yours. The coding-agent workflow
explains how to keep those responsibilities clear.
| Capability | What it gives you |
|---|---|
| MCP servers | Expose Goa service methods as MCP tools and resources, with static prompts and generated JSON-RPC adapters. |
| External tools | Consume MCP servers over stdio or HTTP using declared tool contracts. |
| Tool registries | Consume a named toolset or a changing registry catalog. Generated contracts preserve confirmation, pagination, and exact execution across provider changes. Providers register definitions at startup or attach to a complete declaration saved beforehand, then renew exact leases without resending schemas. Applications can attach immutable catalog identity and use bounded scoped reads without changing declaration encoding or provider messages. Native retry lookups return saved registration or explicit absence. Provider completion reports whether the registry retained the submitted result or settled the execution deadline instead. Handlers can leave an unconfirmed call to registry recovery without inventing a failure or stopping other calls. |
| Deferred tool search | Load definitions on demand using OpenAI native client search with BM25 or Claude hosted search. Consumers choose whole toolsets with Deferred() or exact compiled tools with Deferred("search"), keeping other tools immediately available. Claude replay preserves schema text through JSON escaping while still rejecting changed definitions. |
| Structured output | Declare Completion(...) and get typed unary and streaming helpers. Use typed tool output when you want the same generated result contract with bounded model correction. |
| Standalone JSON codecs | Automatically generate typed encode/decode functions beside supported original Goa types using Goa’s planned shared or service-local declaration, validating complete values and rejecting ambiguous or invalid JSON. |
| Specialist agents | Expose compiled or dynamically configured agents as tools. Child workflows retain the selected configuration and typed result through updates and approval pauses. A resumed child belongs to the continuing parent execution while preserving its original tool call, with linked progress and cancellation. |
| Human input and approval | Ask structured questions or require confirmation, save the pending state, and continue from the answer. |
| Evaluation suites | Generate typed scenario hooks and check actual tool calls, results, and final answers. Preserve results across accepted continuations without counting earlier calls again. Add calibrated model judging for semantic checks. |
| Large tool results | Give models bounded results and runtime-managed pagination; keep rich UI data out of model requests with ServerData. |
| Policies and context | Enforce tool restrictions, call/recovery budgets, and timing. Opt into durable provider recovery before model output, with a separate finite allowance. Configure history compression with instructions to retain critical identifiers verbatim, prompt caching, and prompt overrides. |
| Streaming and observability | Receive assistant text, tool progress, usage, and child-run events in a trusted application host, with OpenTelemetry tracing. Your host selects what to expose to users. |
Use the in-memory engine for local development. For durable execution across worker restarts, configure the Temporal engine and a host-owned durable runtime store. Goa-AI supplies the execution loop, cancellation, policy checks, saved continuations, and tool/child-workflow coordination.
Finish registration before calling runtime.Seal(ctx). Its context bounds waits
for registration or another sealer; engine activation still runs synchronously.
See registration and sealing for
cached success, retry behavior, and the Temporal SDK startup limitation.
Saved continuations preserve completed tool inputs as historical facts. Current input codecs still validate arguments needed for pending execution, successful result materialization or pagination; result codecs remain current. Delayed transcript writes consume the saved result event and its accepted preview, including an empty preview, without rendering old inputs again. Recovery preserves failed arguments as evidence and validates each fresh correction. An input-schema change alone therefore does not invalidate inert history. See continuation compatibility for the checks that still apply.
Choose model adapters for OpenAI, Anthropic, Amazon Bedrock, Google Vertex AI,
or a model gateway. Provider capabilities differ; the runtime guide
covers their supported options. Optional integrations include MongoDB for
memory and prompt overrides, and Redis/Pulse for streams and registries.
For OpenAI Responses on Bedrock, NewBedrockStrictProvider selects strict tool
schemas. Existing NewBedrock and NewBedrockProvider retain complete schemas
with local output validation; constructor choice is fixed, with no fallback.
Strict function tool descriptions explain that null represents an omitted
optional field. The strict compiler requires explicitly closed concrete objects
and complete reference targets or union branches. Unsupported presence rules
and mixed compositions, including fixed-value constraints over structures that
need optional-member removal, fail before transport. Supported required nulls
and explicit values keep their meaning. See the
strict schema contract before upgrading
custom tool or structured-output schemas.
For models that estimate tokens before a call, the usage-reconciled adaptive
limiter admits work from that estimate and corrects its local balance with
tokens reported in the response. Missing usage stays unknown. Shared limiter
startup waits for Redis capacity to reach the local replicated map and fails if
that wait is cancelled or the map stops. Bedrock Responses also estimates GPT-6 Sol and GPT-6.1 Sol
image inputs from dimensions, while response usage remains the accounting total.
Tools can mark typed evidence with NativeImage() to retain exact image sources
in conversation history. Marked evidence uses its declared Goa attribute names
recursively in generated JSON, schemas, field metadata and examples. An explicitly admitted host reader checks current access
and supplies native bytes only for actual count, summary and inference inputs.
Each current tool declaration chooses its image items; retaining an old decoder
does not mark new ordinary evidence. Model clients reject a second reader binding.
See retained native images for registration, bounded read
work and the required historical-decoder upgrade before writing new records.
Adapters can report a locally measured complete-request byte failure with
model.ErrRequestByteCapacity.
History selection may keep fewer optional older turns; required newest and
summary evidence still fail whole when they cannot fit.
Vertex tool arguments require valid UTF-8 text and keys. Workflow writes reject map keys with custom JSON or text encoders; use plain strings or named string types without those encoders. Strict lifecycle and rejection record reads reject invalid raw UTF-8 instead of replacing bytes during JSON decoding. See the JSON boundary contract.
Application code owns planners, service behavior, authorization, side-effect idempotency, storage, and deployment. Deploy generated packages, callers, and workers as a coordinated release. Read the production configuration, workflow result migration, and upgrade guide before replacing existing workers.
Existing registries need an offline catalog storage conversion with all old writers stopped. The new layout separates current definitions, compact provider state, and permanent retired tokens. Wire protocol 10 and fingerprints stay unchanged; catalog, call, and retirement history must be preserved. Providers require the duration-only Renew callback and stop if their lease authority is lost. The preview guide states the conversion prerequisites; it does not yet supply a published converter.
Registry result readers use toolregistry.DecodeToolResultMessage to reject
unknown fields in fixed wire records while preserving raw tool values for their
existing codecs. This tightens acceptance of external and retained messages;
verify those messages before upgrading. Decoding alone does not establish a
complete tool result. After envelope and identity validation, adapters can call
runtime.ValidateSuccessfulToolResult to reuse the selected tool's existing
semantic-result and bounds checks before forwarding a successful result.
Server-data validation remains separate. See the result envelope contract
for identity, retry, parser semantics, and tool-value validation responsibilities.
Provider hosts can use toolprovider.ServeAndWait with the same arguments as
Serve to join invocation-owned work before returning. Serve keeps its bounded
settlement return. The joined call may wait longer for dependencies, preserves
incomplete-settlement errors, and never releases a lease merely because late work
finishes. See provider execution
for the lifecycle and sink cleanup responsibilities.
- Goa-AI documentation — guided learning paths.
- Generated quickstart guide — inspect what generation produces for a real application.
- DSL reference — agents, tools, completions, MCP, registries, and policies.
- Runtime reference — planners, engines, model clients, storage, and execution contracts.
- Architecture and generated artifact layout — how the framework fits together.
- Go package reference and feature packages.
- Goa services — the other entry point into the ecosystem.
Issues and PRs are welcome. Include a Goa design, a failing test, or a clear reproduction when reporting behavior. See AGENTS.md for repository guidelines.
Run make setup once after cloning or when .tool-versions or .go-install
changes. It installs the exact protobuf compiler and Go generators used by CI.
Normal make targets verify those versions before building or generating code.
MIT License (C) Raphael Simon and the Goa community.