梦兽编程
AI_SUITE

Artifacts: Structured Content Generation

Detailed explanation of Artifacts usage in ADK Go—letting Agent generate structured content like code, Markdown, HTML.

When an Agent’s output is not just conversational text for humans but machine-readable content that downstream systems need to parse, render, or execute, the concept of Artifacts becomes essential. Artifacts are the core mechanism in ADK Go for generating structured, typed, and reusable content outputs. They let an Agent produce code snippets, JSON configurations, HTML pages, Markdown documents, and other content with a clear format and semantics, rather than code blocks embedded in free-form text.

From Free Text to Structured Output: Why Artifacts Are Needed

In traditional LLM interaction, if you ask the model to “write a Go function to handle HTTP requests”, it might reply like this:

Here is a Go function for handling HTTP requests:

```go
func handleRequest(w http.ResponseWriter, r *http.Request) {
    // ...
}

You can use it in main.go. Remember to import the net/http package.


This output is friendly to humans, but extracting the code block programmatically requires extra parsing logic and is error-prone (for example, the code block may contain explanatory text). The Artifacts mechanism solves this by making the model explicitly mark "this is an independent, extractable content unit."

From an architectural perspective, Artifacts realize the leap from unstructured narrative to structured data in LLM output. This enables:

1. **Frontend rendering optimization**: The UI can use dedicated renderers for different Artifact types (syntax highlighting, chart drawing, table display).
2. **Downstream system integration**: Generated JSON can be consumed directly by APIs, and generated SQL can be executed directly.
3. **Version control and tracking**: Each Artifact has an independent identifier and can be tracked, updated, and rolled back individually.
4. **Multimodal workflows**: One Artifact can be code, while another can be a test case for that code, and references can be established between them.

## Artifacts Architecture in ADK Go

The ADK Go Artifacts system consists of three core components:

**Artifact definition**: describes content metadata such as type, title, and language.
**Artifact content**: the actual data payload (string or binary).
**Artifact handler**: responsible for converting an Artifact into the final output format.

User request ↓ [Agent reasoning] — the model decides which Artifacts to generate ↓ [Artifact generation] — each Artifact is generated independently with a type label ↓ [Artifact processing] — render, validate, or transform based on type ↓ [Output assembly] — combine Artifacts with narrative text into the final response


Unlike simple "embedding code blocks in Markdown", Artifacts are first-class citizens at the protocol level. This means the client knows precisely "here is an Artifact of type `application/vnd.go` titled `handler.go`."

## Basic Usage: Enabling Artifact Generation

Enabling Artifacts in ADK Go requires explicitly declaring them in the Agent configuration. Below is a complete, production-ready configuration example:

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "google.golang.org/adk/agent"
    "google.golang.org/adk/artifact"
    "google.golang.org/adk/llm"
)

func main() {
    ctx := context.Background()

    model, err := llm.NewGeminiModel(ctx, llm.GeminiConfig{
        APIKey: os.Get...Y"),
        Model:  "gemini-2.0-pro",
    })
    if err != nil {
        log.Fatalf("Failed to initialize model: %v", err)
    }

    // Register supported Artifact types
    registry := artifact.NewRegistry()
    registry.Register(artifact.TypeCode, artifact.CodeHandler{
        Languages: []string{"go", "python", "javascript", "sql"},
    })
    registry.Register(artifact.TypeMarkdown, artifact.MarkdownHandler{})
    registry.Register(artifact.TypeJSON, artifact.JSONHandler{
        SchemaValidator: true,  // enable JSON Schema validation
    })
    registry.Register(artifact.TypeHTML, artifact.HTMLHandler{
        Sanitize: true,  // apply XSS filtering to HTML
    })

    agent, err := agent.New(agent.Config{
        Name:  "code-generator",
        Model: model,
        Instruction: `You are a full-stack development assistant. When the user requests code, documentation, or configuration, you must output the generated structured content as Artifacts.

Artifact usage rules:
1. Every Artifact must have an explicit type and title.
2. Code Artifacts must specify the programming language.
3. Multiple related files should be output as independent Artifacts.
4. Provide brief explanatory text before and after each Artifact.`,
        Artifacts: artifact.Config{
            Enabled:   true,
            Registry:  registry,
            MaxCount:  10,  // at most 10 Artifacts per response
            MaxSize:   64 * 1024,  // each Artifact up to 64 KB
        },
    })
    if err != nil {
        log.Fatalf("Failed to create Agent: %v", err)
    }

    resp, err := agent.Run(ctx, "Write a Go HTTP service with user registration and login endpoints")
    if err != nil {
        log.Fatalf("Execution failed: %v", err)
    }

    // Iterate over Artifacts in the response
    for _, art := range resp.Artifacts {
        fmt.Printf("Artifact: %s (%s)\n", art.Title, art.Type)
        fmt.Println("---")
        fmt.Println(art.Content)
        fmt.Println("---")
    }
}

Built-in Artifact Types

Type IDMIME TypeUse CaseSpecial Handling
codeapplication/vnd.codeSource-code filesSyntax highlighting, language identification
markdowntext/markdownDocumentation, notesMarkdown rendering
jsonapplication/jsonConfigurations, dataSchema validation
htmltext/htmlFrontend pages, email templatesXSS filtering
svgimage/svg+xmlVector graphicsRender preview
mermaidtext/x-mermaidFlowcharts, architecture diagramsDiagram rendering

Advanced Pattern: Custom Artifact Types and Handlers

Built-in Artifact types are often insufficient in production. ADK Go allows registering custom Artifact types and handlers. Below is a custom Artifact example designed for Infrastructure-as-Code (IaC) scenarios:

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "regexp"

    "google.golang.org/adk/agent"
    "google.golang.org/adk/artifact"
)

// TerraformArtifact represents a Terraform configuration file
type TerraformArtifact struct {
    Title       string
    Content     string
    Provider    string  // aws, gcp, azure
    Resources   []string
}

// TerraformHandler validates and transforms Terraform Artifacts
type TerraformHandler struct {
    // You can inject the Terraform binary path for syntax validation
    TerraformPath string
}

func (h *TerraformHandler) Type() string {
    return "application/vnd.terraform"
}

func (h *TerraformHandler) Validate(art *artifact.Artifact) error {
    // Basic syntax check: ensure valid HCL format
    if !regexp.MustCompile(`^\s*(resource|data|module|variable|output|provider)\s+"`).MatchString(art.Content) {
        return fmt.Errorf("invalid Terraform configuration: missing resource definition")
    }

    // Production recommendation: call terraform validate for full validation
    // Simplified here for the example
    return nil
}

func (h *TerraformHandler) Render(art *artifact.Artifact) (string, error) {
    // Return syntax-highlighted HTML or plain text
    return fmt.Sprintf("```hcl\n%s\n```", art.Content), nil
}

func (h *TerraformHandler) Transform(art *artifact.Artifact, target string) (*artifact.Artifact, error) {
    // Support conversion to other formats, e.g. JSON (equivalent of terraform show -json)
    switch target {
    case "json":
        // Call hcl2json or a similar tool
        return nil, fmt.Errorf("JSON conversion not yet implemented")
    default:
        return nil, fmt.Errorf("unsupported conversion target: %s", target)
    }
}

func main() {
    registry := artifact.NewRegistry()
    registry.Register("application/vnd.terraform", &TerraformHandler{
        TerraformPath: "/usr/local/bin/terraform",
    })

    // ... create Agent and use registry
}

The core value of custom Artifact handlers is encapsulating domain-specific validation and transformation logic inside the type system. For example, an SQL Artifact handler can:

  • Use an SQL parser to check syntax correctness.
  • Identify dangerous operations (DROP, DELETE without WHERE).
  • Format SQL according to team conventions.
  • Generate corresponding ORM code as a related Artifact.

Production Practice: Multi-file Project Generation

In real-world development, an Agent often needs to generate a complete project structure made up of multiple files. The Artifacts mechanism naturally supports this pattern. Below is an example of generating a complete Go Web project scaffold:

func generateProject(ctx context.Context, agent *agent.Agent, description string) (*Project, error) {
    resp, err := agent.Run(ctx, fmt.Sprintf(`
Generate a complete Go Web project based on the following description:
%s

Requirements:
1. Generate main.go, handlers.go, models.go, and go.mod as separate files
2. Output each file as an independent Artifact
3. Include appropriate error handling and logging
4. Use the standard library net/http and no external dependencies
`, description))
    if err != nil {
        return nil, err
    }

    proj := &Project{Files: make(map[string]string)}
    for _, art := range resp.Artifacts {
        if art.Type != "application/vnd.code" {
            continue
        }
        // Assume the title is the file name
        proj.Files[art.Title] = art.Content
    }

    // Validate project completeness
    requiredFiles := []string{"main.go", "handlers.go", "models.go", "go.mod"}
    for _, f := range requiredFiles {
        if _, ok := proj.Files[f]; !ok {
            return nil, fmt.Errorf("generated project is missing required file: %s", f)
        }
    }

    return proj, nil
}

type Project struct {
    Files map[string]string
}

func (p *Project) WriteToDisk(basePath string) error {
    for filename, content := range p.Files {
        path := filepath.Join(basePath, filename)
        if err := os.WriteFile(path, []byte(content), 0644); err != nil {
            return fmt.Errorf("failed to write %s: %w", filename, err)
        }
    }
    return nil
}

Cross-Artifact References

In complex projects, Artifacts may depend on each other. For example, main.go imports the handlers package. ADK Go supports declaring such relationships explicitly through the Dependencies field:

// Declare dependencies in Artifact metadata
art := &artifact.Artifact{
    Title: "main.go",
    Type:  "application/vnd.code",
    Content: mainGoContent,
    Metadata: map[string]interface{}{
        "language": "go",
        "dependencies": []string{"handlers.go", "models.go"},
    },
}

This allows downstream systems to:

  • Process Artifacts in dependency order (models first, then handlers, then entry point).
  • Build an Artifact dependency graph and detect circular dependencies.
  • Identify all downstream Artifacts affected when one Artifact is updated.

Security Considerations: Handling Untrusted Content

When an Agent generates HTML, JavaScript, SQL, or similar content, security risks increase significantly. The Artifacts system provides multiple layers of protection:

1. XSS Protection for HTML/SVG

htmlHandler := artifact.HTMLHandler{
    Sanitize: true,
    // Use a strict HTML sanitizer such as bluemonday
    AllowedTags: []string{"p", "br", "strong", "em", "code", "pre"},
    AllowedAttrs: map[string][]string{
        "a": {"href"},
    },
}

2. SQL Injection Prevention

For SQL Artifacts, never execute Agent-generated SQL directly. The correct approach is:

func safeExecuteSQL(db *sql.DB, sqlArt *artifact.Artifact) error {
    // 1. Analyze statement type with an SQL parser
    stmt, err := sqlparser.Parse(sqlArt.Content)
    if err != nil {
        return fmt.Errorf("SQL parsing failed: %w", err)
    }

    // 2. Only allow operations on a whitelist
    switch stmt.(type) {
    case *sqlparser.Select:
        // SELECT is safe (read-only)
    default:
        return fmt.Errorf("non-SELECT statements are not allowed")
    }

    // 3. Execute on a read-only replica
    rows, err := db.Query(sqlArt.Content)
    // ...
}

3. Code Execution Isolation

If the system needs to execute Agent-generated code (for example, a Python script), it must run in a sandboxed environment:

func executeInSandbox(code string, timeout time.Duration) (*SandboxResult, error) {
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    cmd := exec.CommandContext(ctx, "firejail", "--noprofile", "python3", "-c", code)
    cmd.Env = []string{}  // clear environment variables

    output, err := cmd.CombinedOutput()
    // ...
}

Debugging and Quality Assurance

Debugging Artifacts is more complex than debugging plain text output because you need to verify structural correctness, semantic consistency, and cross-Artifact coordination. Implement the following quality-assurance measures:

Automated Validation Pipeline

func validateArtifacts(artifacts []artifact.Artifact) []ValidationError {
    var errors []ValidationError

    for _, art := range artifacts {
        handler := registry.Get(art.Type)
        if handler == nil {
            errors = append(errors, ValidationError{
                Artifact: art.Title,
                Message:  fmt.Sprintf("unknown Artifact type: %s", art.Type),
            })
            continue
        }

        if err := handler.Validate(&art); err != nil {
            errors = append(errors, ValidationError{
                Artifact: art.Title,
                Message:  err.Error(),
            })
        }
    }

    // Cross-Artifact validation: check reference consistency
    errors = append(errors, validateCrossReferences(artifacts)...)

    return errors
}

Version Control and Diff

Track generated Artifacts in version control to monitor how Agent output changes over time:

func diffArtifacts(old, new []artifact.Artifact) []ArtifactDiff {
    // Match by title and compute text differences
    // Use libraries such as github.com/sergi/go-diff/diffmatchpatch
}

Performance Optimization

When using Artifacts at scale, watch for the following performance issues:

  1. Token consumption: Artifacts are usually more structured than plain text and may contain more redundant characters (indentation, braces, etc.). Monitor token usage and compress or shard large Artifacts when necessary.

  2. Rendering latency: Rendering complex Artifacts (such as large Mermaid diagrams) on the frontend can block the UI. Optimize with Web Workers or virtual lists.

  3. Storage overhead: If you persist all historical Artifacts, consider object storage (S3, GCS) instead of a relational database.

Next Steps

Artifacts solve the problem of how the Agent outputs—through structured, typed content generation so that Agent output can be reliably consumed and processed by programs. Next we will explore Skills—how to equip an Agent with preset professional capability modules so it performs more expertly in specific domains.

Grounding | Skills for Agents →


Want to learn more Go ADK hands-on? Follow the “Full-Stack Peak — Mengshou Programming” WeChat account for weekly Go / AI programming practice updates.

Frequently Asked Questions

What are Artifacts?

Artifacts are a mechanism in ADK Go for generating structured, typed, reusable content such as code, JSON, HTML, and Markdown.

Why use Artifacts instead of plain text?

Machine-readable structured output can be parsed, rendered, or executed by downstream systems without manually extracting code blocks from conversation text.

Which built-in Artifact types does ADK Go support?

Common built-in types include code snippets, JSON configurations, HTML pages, and Markdown documents.

How do I define custom Artifact types and handlers?

Implement the Artifact interface and register a handler to extend new types and rendering logic.

How do I handle references between Artifacts when generating multi-file projects?

Use Artifact IDs and dependency relationships to generate mutually-referencing multi-file project structures.