Chapter 1: ChatModel and Message (Console)

Introduction to the Eino Framework

What is Eino?

Eino is an AI application development framework (Agent Development Kit) implemented in Go, designed to help developers quickly build extensible, maintainable AI applications.

What problems does Eino solve?

  1. Model abstraction: Unifies interfaces across different LLM providers (OpenAI, Ark, Claude, etc.), allowing model switching without modifying business code
  2. Capability composition: Implements replaceable, composable capability units (conversation, tools, retrieval, etc.) through Component interfaces
  3. Orchestration framework: Provides orchestration abstractions like Agent, Graph, and Chain, supporting complex multi-step AI workflows
  4. Runtime support: Built-in streaming output, interrupt and resume, state management, Callback observability, and more

Eino’s main repositories:

  • eino (this repository): Core library, defines interfaces, orchestration abstractions, and ADK
  • eino-ext: Extension library, provides concrete implementations of various Components (OpenAI, Ark, Milvus, etc.)
  • eino-examples: Example code repository, containing this quickstart series

ChatWithEino: An Intelligent Assistant for Conversing with Eino Documentation

What is ChatWithEino?

ChatWithEino is an intelligent assistant built on the Eino framework that helps developers learn the Eino framework and write Eino code. It provides the most accurate and timely technical support by accessing source code, comments, and examples from the Eino repository.

Core capabilities:

  • Conversational interaction: Understands user questions about Eino and provides clear answers
  • Code access: Directly reads Eino source code, comments, and examples, answering questions based on real implementations
  • Persistent sessions: Supports multi-turn conversations, remembers context, and can resume sessions across processes
  • Tool calling: Can perform file reading, code searching, and other operations

Technical architecture:

  • ChatModel: Communicates with large language models (OpenAI, Ark, Claude, etc.)
  • Tool: Capability extensions like file system access and code search
  • Memory: Persistent storage of conversation history
  • Agent: Unified execution framework coordinating components to work together

Quickstart Documentation Series: Building ChatWithEino from Scratch

This documentation series takes you through a progressive approach, starting from the most basic ChatModel call and gradually building a fully-featured ChatWithEino Agent.

Learning path:

ChapterTopicCore ContentCapability Gained
Chapter 1ChatModel and AgenticMessageUnderstand Component abstraction, implement single-turn conversationBasic conversation
Chapter 2Agent and RunnerIntroduce execution abstraction, implement multi-turn conversationSession management
Chapter 3Memory and SessionPersist conversation history, support session recoveryPersistence
Chapter 4Tool and File SystemAdd file access capability, read source codeTool calling
Chapter 5MiddlewareMiddleware mechanism, unified handling of cross-cutting concernsExtensibility
Chapter 6CallbackCallback mechanism, monitor Agent executionObservability
Chapter 7Interrupt and ResumeInterrupt and resume, support long-running tasksReliability
Chapter 8Graph and ToolUse Graph to orchestrate complex workflowsComplex orchestration
Chapter 9SkillUse Skill middleware to load and reuse skill documentsKnowledge reuse
FinalA2UIAgent-to-UI integration solutionProduction-grade application

Why this design?

Each chapter adds one core capability on top of the previous one, allowing you to:

  1. Understand the role of each component: Rather than showing all features at once, they are introduced progressively
  2. See the architecture evolution: From simple to complex, understanding why each abstraction is needed
  3. Master practical development skills: Each chapter has runnable code for hands-on practice

This chapter’s goal: Understand Eino’s Component abstraction, call a ChatModel with minimal code (with streaming output support), and learn how to organize model input and streaming output using schema.AgenticMessage.

Code Location

Why Component Interfaces Are Needed

Eino defines a set of Component interfaces (ChatModel, Tool, Retriever, Loader, etc.), each describing a category of replaceable capabilities:

type BaseModel[M any] interface {
    Generate(ctx context.Context, input []M, opts ...Option) (M, error)
    Stream(ctx context.Context, input []M, opts ...Option) (*schema.StreamReader[M], error)
}

type AgenticModel = BaseModel[*schema.AgenticMessage]

Benefits of interfaces:

  1. Replaceable implementations: eino-ext provides multiple implementations including OpenAI, Ark, Claude, Ollama, etc. Business code only depends on the interface; switching models only requires changing the construction logic.
  2. Composable orchestration: Orchestration layers like Agent, Graph, and Chain only depend on Component interfaces, not caring about specific implementations. You can swap OpenAI for Ark without changing orchestration code.
  3. Mockable for testing: Interfaces naturally support mocking; unit tests don’t need real model calls.

This chapter only involves ChatModel; subsequent chapters will progressively introduce Tool, Retriever, and other Components.

The example code defaults to using model.AgenticModel, which is model.BaseModel[*schema.AgenticMessage]. This allows subsequent chapters to express text, reasoning, tool calls, tool results, and more within the same message structure.

schema.AgenticMessage: The Basic Unit of Conversation

AgenticMessage is the conversation data structure used in this Quickstart:

In a single model call, the model may return multiple ordered events — for example, first outputting reasoning, then calling a server tool, followed by more reasoning, then calling a function tool. AgenticMessage stores these structured events in order using ContentBlock.

type AgenticMessage struct {
    Role          AgenticRoleType
    ContentBlocks []*ContentBlock
    ResponseMeta  *AgenticResponseMeta
    Extra         map[string]any
}

type ContentBlock struct {
    Type               ContentBlockType
    Reasoning          *Reasoning
    UserInputText      *UserInputText
    AssistantGenText   *AssistantGenText
    FunctionToolCall   *FunctionToolCall
    FunctionToolResult *FunctionToolResult
    // ...
}

Common constructors:

schema.SystemAgenticMessage("You are a helpful assistant.")
schema.UserAgenticMessage("What is the weather today?")

&schema.AgenticMessage{
    Role: schema.AgenticRoleTypeAssistant,
    ContentBlocks: []*schema.ContentBlock{
        schema.NewContentBlock(&schema.AssistantGenText{Text: "I don't know."}),
    },
}

Role semantics:

  • system: System instructions, typically placed at the beginning of the message list
  • user: User input
  • assistant: Model response
  • Tool calls and tool results are expressed through function_tool_call / function_tool_result content blocks (covered in later chapters)

Prerequisites

Get the Code

git clone https://github.com/cloudwego/eino-examples.git
cd eino-examples/quickstart/chatwitheino
  • Go version: Go 1.21+ (see go.mod)
  • A callable ChatModel (defaults to OpenAI; Ark is also supported)

Option A: OpenAI (Default)

export OPENAI_API_KEY="..."
export OPENAI_MODEL="gpt-4.1-mini"  # OpenAI 2025 new model, gpt-4o or gpt-4o-mini also work
# Optional:
# OPENAI_BASE_URL (proxy or compatible service)
# OPENAI_BY_AZURE=true (use Azure OpenAI)

Option B: Ark

export MODEL_TYPE="ark"
export ARK_API_KEY="..."
export ARK_MODEL="..."
# Optional: ARK_BASE_URL

Running

In the examples/quickstart/chatwitheino directory:

go run ./cmd/ch01 -- "Explain in one sentence what problem Eino's Component design solves."

Example output (streamed progressively):

[assistant] Eino's Component design solves the problem of...

What the Entry Code Does

In execution order:

  1. Create ChatModel: Select OpenAI or Ark’s agentic model based on the MODEL_TYPE environment variable
  2. Construct input messages: Create AgenticMessage using msgops.NewSystem[M] / msgops.NewUser[M]
  3. Call Stream: Use model.BaseModel[M].Stream(), returning a StreamReader[M]
  4. Print results: Iterate the StreamReader to print assistant replies frame by frame

Key code snippet (Note: This is a simplified code snippet that cannot run directly. For the complete code, please refer to cmd/ch01/main.go):

func runTyped[M adk.MessageType](ctx context.Context, instruction, query string) {
    cm, err := chatmodel.NewModel[M](ctx)
    if err != nil {
        log.Fatal(err)
    }

    messages := []M{
        msgops.NewSystem[M](instruction),
        msgops.NewUser[M](query),
    }

    stream, err := cm.Stream(ctx, messages)
    if err != nil {
        log.Fatal(err)
    }
    defer stream.Close()

    for {
        frame, err := stream.Recv()
        if errors.Is(err, io.EOF) {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        fmt.Print(msgops.AssistantDeltaText(frame))
    }
}

Chapter Summary

  • Component interface: Defines replaceable, composable, and testable capability boundaries
  • AgenticMessage: The basic unit of conversation data, distinguishing semantics through roles and content blocks
  • ChatModel: The most fundamental Component, providing two core methods: Generate and Stream
  • Implementation selection: Switch between different implementations like OpenAI/Ark via environment variables or configuration, with no changes needed in business code