助手
扣子 AI 帮助与支持
你好,我是 扣子 文档问答助手 🎉 你在阅读当前文档的过程中,无论对文档概念的解释,还是文档内容方面的疑问,都可以随时向我提问,我会全力为你解答
推荐问题
如何快速了解这个空间的核心内容?
有哪些近期更新的重点文档?
我应该从哪些文档开始阅读?
文档反馈

Eino

更新于: 2026-06-24 15:45:09

本文介绍如何上报 Eino 运行过程的 Trace 数据到扣子罗盘,实现对 AI 应用的监控和分析。

功能介绍

Eino 是一个基于 Golang 语言开发的大模型应用开发框架。扣子罗盘基于 Eino Callback 机制与 Eino 集成,捕获 Eino 应用执行过程的 Trace 数据。上报 Trace 数据到扣子罗盘后,你可以在扣子罗盘中进行可视化分析。本集成方案适用于需要监控 Eino 应用执行链路、分析 AI 交互全链路情况的场景。

配置环境变量

在上报 Trace 数据前,你需要正确配置环境变量,以确保 Eino 应用能够正常调用 OpenAI 模型,并能正确发送 Trace 数据到指定的扣子罗盘空间中。环境变量配置格式及说明如下:

环境变量

说明

COZELOOP_API_TOKEN

设置上报数据时所需的扣子罗盘认证信息,即配置为扣子罗盘的个人访问令牌或服务访问令牌。获取步骤,请参考配置个人访问令牌配置服务访问令牌

COZELOOP_WORKSPACE_ID

配置为扣子罗盘工作空间 ID。获取步骤,请参考获取扣子罗盘工作空间 ID

OPENAI_BASE_URL

配置 OpenAI 模型的服务地址。

  • 推荐使用方舟模型,其 BASE_URL 固定为 https://ark.cn-beijing.volces.com/api/v3
  • 使用其他 OpenAI 模型时,配置为其对应的 BASE_URL 即可。

OPENAI_API_KEY

配置 OpenAI 模型的 API Key。

  • 推荐使用方舟模型,其 API Key 获取步骤,请参考 API Key 管理
  • 使用其他 OpenAI 模型时,配置为其对应的 API Key 即可。

OPENAI_MODEL_NAME

配置 OpenAI 模型名称。

上报 Trace

完成上述配置后,你可以调用 OpenAI 模型开展 AI 对话,并通过扣子罗盘 SDK 上报 Trace 数据到扣子罗盘。
在本示例中,Eino 框架基于 OpenAI 模型,实现了简单的用户信息查询和房产推荐功能。同时,借助扣子罗盘 SDK 将模型调用、工具执行等关键步骤的 Trace 数据自动上报到扣子罗盘,便于开发者监控和分析 AI 交互全链路的执行情况。

import (
    "context"
    "errors"
    "os"

    clc "github.com/cloudwego/eino-ext/callbacks/cozeloop"
    "github.com/cloudwego/eino-ext/components/model/openai"
    "github.com/cloudwego/eino/callbacks"
    "github.com/cloudwego/eino/components/prompt"
    "github.com/cloudwego/eino/components/tool"
    "github.com/cloudwego/eino/components/tool/utils"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
    "github.com/coze-dev/cozeloop-go"

    "github.com/cloudwego/eino-examples/internal/gptr"
    "github.com/cloudwego/eino-examples/internal/logs"
)

func main() {
    openAIBaseURL := os.Getenv("OPENAI_BASE_URL")
    openAIAPIKey := os.Getenv("OPENAI_API_KEY")
    modelName := os.Getenv("OPENAI_MODEL_NAME")
    cozeloopApiToken := os.Getenv("COZELOOP_API_TOKEN")
    cozeloopWorkspaceID := os.Getenv("COZELOOP_WORKSPACE_ID") // use cozeloop trace, from https://loop.coze.cn/open/docs/cozeloop/go-sdk#4a8c980e
    
    userQuery := "我叫zhangsan, 邮箱是zhangsan@xxx.com, 查询我的信息"
    
    // init cozeloop client
    ctx := context.Background()
    var handlers []callbacks.Handler
    if cozeloopApiToken != "" && cozeloopWorkspaceID != "" {
       client, err := cozeloop.NewClient(
          cozeloop.WithAPIToken(cozeloopApiToken),
          cozeloop.WithWorkspaceID(cozeloopWorkspaceID),
       )
       if err != nil {
          panic(err)
       }
       defer client.Close(ctx)
       handlers = append(handlers, clc.NewLoopHandler(client))
    }
    // 只需在程序开始处设置一次GlobalHandler,后续就可以自动检测并上报
    callbacks.AppendGlobalHandlers(handlers...)
    
    // 后续完成Eino Graph逻辑,这里是一个查询用户信息的model+tool的示例
    systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的`
    chatTpl := prompt.FromMessages(schema.FString,
       schema.SystemMessage(systemTpl),
       schema.MessagesPlaceholder("message_histories", true),
       schema.UserMessage("{query}"),
    )

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
       BaseURL:     openAIBaseURL,
       APIKey:      openAIAPIKey,
       Model:       modelName,
       Temperature: gptr.Of(float32(0.7)),
    })
    if err != nil {
       logs.Fatalf("NewChatModel failed, err=%v", err)
    }

    userInfoTool := utils.NewTool(
       &schema.ToolInfo{
          Name: "user_info",
          Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
          ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
             "name": {
                Type: "string",
                Desc: "用户的姓名",
             },
             "email": {
                Type: "string",
                Desc: "用户的邮箱",
             },
          }),
       },
       func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
          return &userInfoResponse{
             Name:     input.Name,
             Email:    input.Email,
             Company:  "Awesome company",
             Position: "CEO",
             Salary:   "9999",
          }, nil
       })

    info, err := userInfoTool.Info(ctx)
    if err != nil {
       logs.Fatalf("Get ToolInfo failed, err=%v", err)
    }

    err = chatModel.BindTools([]*schema.ToolInfo{info})
    if err != nil {
       logs.Fatalf("BindTools failed, err=%v", err)
    }

    toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
       Tools: []tool.BaseTool{userInfoTool},
    })
    if err != nil {
       logs.Fatalf("NewToolNode failed, err=%v", err)
    }

    takeOne := compose.InvokableLambda(func(ctx context.Context, input []*schema.Message) (*schema.Message, error) {
       if len(input) == 0 {
          return nil, errors.New("input is empty")
       }
       return input[0], nil
    })

    const (
       nodeModel     = "node_model"
       nodeTools     = "node_tools"
       nodeTemplate  = "node_template"
       nodeConverter = "node_converter"
    )

    branch := compose.NewStreamGraphBranch(func(ctx context.Context, input *schema.StreamReader[*schema.Message]) (string, error) {
       defer input.Close()
       msg, err := input.Recv()
       if err != nil {
          return "", err
       }

       if len(msg.ToolCalls) > 0 {
          return nodeTools, nil
       }

       return compose.END, nil
    }, map[string]bool{compose.END: true, nodeTools: true})

    graph := compose.NewGraph[map[string]any, *schema.Message]()

    _ = graph.AddChatTemplateNode(nodeTemplate, chatTpl)
    _ = graph.AddChatModelNode(nodeModel, chatModel)
    _ = graph.AddToolsNode(nodeTools, toolsNode)
    _ = graph.AddLambdaNode(nodeConverter, takeOne)

    _ = graph.AddEdge(compose.START, nodeTemplate)
    _ = graph.AddEdge(nodeTemplate, nodeModel)
    _ = graph.AddBranch(nodeModel, branch)
    _ = graph.AddEdge(nodeTools, nodeConverter)
    _ = graph.AddEdge(nodeConverter, compose.END)

    r, err := graph.Compile(ctx)
    if err != nil {
       logs.Fatalf("Compile failed, err=%v", err)
    }

    out, err := r.Invoke(ctx, map[string]any{"query": userQuery})
    if err != nil {
       logs.Fatalf("Invoke failed, err=%v", err)
    }

    logs.Infof("result content: %v", out.Content)
}

type userInfoRequest struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

type userInfoResponse struct {
    Name     string `json:"name"`
    Email    string `json:"email"`
    Company  string `json:"company"`
    Position string `json:"position"`
    Salary   string `json:"salary"`
}

查看 Trace

上报 Trace 数据后,你可以在扣子罗盘Trace 页面,找到并单击目标 Span,查看上报的 Trace 数据。
Image

更多示例

关于上报 Eino Trace 数据的更多示例,请参考 Eino