在线咨询
专属客服在线解答,提供专业解决方案
工单支持
专业技术支持团队,随时响应服务需求

Go + Gin 搭建语音智能体服务端

如果你的后端是 Go,这篇教程给你一个能直接嵌进现有服务的最小实现:用官方 Go Agent SDK 起三个接口——/get_config/startAgent/stopAgent

浏览器端直接复用 《Next.js 搭建浏览器端语音智能体》那份客户端,不用另写。App Certificate、DeepSeek 和 MiniMax Key 全程留在 Go 服务端。

跑通之后,中文开场白、多轮对话、双方实时字幕、正常停止,一样不少。


一. 架构与准备工作

浏览器 RTC/RTM ──→ Go Gin API
                      │ agentkit/cn
                      ▼
                 声网对话式 AI 云端
        凤鸣 ASR → DeepSeek → MiniMax TTS

需要 Go 1.23、App ID、主要证书、DeepSeek 和 MiniMax Key。依赖固定这两个版本:

github.com/AgoraIO/agora-agents-go/v2 v2.3.0
github.com/gin-gonic/gin v1.11.0

二. 环境变量

AGORA_APP_ID=REPLACE_WITH_SHENGWANG_APP_ID
AGORA_APP_CERTIFICATE=REPLACE_WITH_PRIMARY_CERTIFICATE
DEEPSEEK_API_KEY=REPLACE_WITH_DEEPSEEK_KEY
MINIMAX_API_KEY=REPLACE_WITH_MINIMAX_KEY
WEB_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
PORT=8000

客户端只能拿到 App ID、频道、UID 和短期 Token,其余三项凭证一律不下发。


三. 初始化客户端

Go SDK 和其他语言这里不太一样。入口不是在全局客户端上手写 URL,而是直接用 agentkit/cn 包,它会自动选好区域和接口路径。

package main

import (
    "os"

    agentkit "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn"
)

func newChinaClient() *agentkit.AgoraClient {
    return agentkit.NewAgoraClient(agentkit.ClientOptions{
        AppID:          os.Getenv("AGORA_APP_ID"),
        AppCertificate: os.Getenv("AGORA_APP_CERTIFICATE"),
    })
}

四. 配置凤鸣、DeepSeek 和 MiniMax

package main

import (
    "os"

    agora "github.com/AgoraIO/agora-agents-go/v2"
    agentkit "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn"
    vendors "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn/vendors"
)

func intPtr(value int) *int           { return &value }
func floatPtr(value float64) *float64 { return &value }
func boolPtr(value bool) *bool        { return &value }

func buildAgent(client *agentkit.AgoraClient) *agentkit.Agent {
    language := agora.AsrLanguage("zh-CN")
    dataChannel := agentkit.ParametersDataChannel("rtm")
    llm := vendors.NewDeepSeek(vendors.DeepSeekOptions{
        APIKey:  os.Getenv("DEEPSEEK_API_KEY"),
        BaseURL: "https://api.deepseek.com/chat/completions",
        Model:   "deepseek-v4-flash",
        SystemMessages: []map[string]interface{}{{
            "role": "system",
            "content": "你是中文语音助手,回答自然、简短。",
        }},
        GreetingMessage: "你好,我是你的声网 Go 语音助手。",
        FailureMessage:  "抱歉,服务暂时不可用,请稍后再试。",
        MaxHistory:      intPtr(20),
        MaxTokens:       intPtr(512),
        Temperature:     floatPtr(0.5),
        Params: map[string]interface{}{
            "thinking": map[string]interface{}{"type": "disabled"},
        },
    })
    tts := vendors.NewMiniMaxTTS(vendors.MiniMaxTTSOptions{
        Key:   os.Getenv("MINIMAX_API_KEY"),
        Model: "speech-01-turbo",
        VoiceSetting: &vendors.MiniMaxVoiceSetting{
            VoiceID: "female-shaonv",
        },
        AudioSetting: &vendors.MiniMaxAudioSetting{
            SampleRate: 16000,
        },
        LanguageBoost: "Chinese",
    })
    return agentkit.NewAgent(
        client,
        agentkit.WithTurnDetectionConfig(
            &agentkit.TurnDetectionConfig{Language: &language},
        ),
        agentkit.WithAdvancedFeatures(&agentkit.AdvancedFeatures{
            EnableRtm: boolPtr(true),
        }),
        agentkit.WithParameters(&agentkit.SessionParams{
            DataChannel:        &dataChannel,
            EnableMetrics:      boolPtr(true),
            EnableErrorMessage: boolPtr(true),
        }),
        agentkit.WithAudioScenario(
            agentkit.ParametersAudioScenario("chorus"),
        ),
    ).WithStt(
        vendors.NewFengmingSTT(),
    ).WithLlm(llm).WithTts(tts)
}

包名仍然是 AgoraIO,别机械地替换成别的。区域是由 agentkit/cn 决定的,不是由包名决定的。


五. 创建和停止会话

package main

import (
    "context"
    "strconv"
    "time"

    agentkit "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn"
)

func start(
    agent *agentkit.Agent,
    channel string,
    agentUID int,
    userUID int,
) (string, *agentkit.AgentSession, error) {
    expiresIn, err := agentkit.ExpiresInHours(1)
    if err != nil {
        return "", nil, err
    }
    idleTimeout := 120
    enableStringUID := false
    session := agent.CreateSession(agentkit.CreateSessionOptions{
        Channel:         channel,
        AgentUID:        strconv.Itoa(agentUID),
        RemoteUIDs:      []string{strconv.Itoa(userUID)},
        IdleTimeout:     &idleTimeout,
        EnableStringUID: &enableStringUID,
        ExpiresIn:       expiresIn,
    })
    ctx, cancel := context.WithTimeout(
        context.Background(), 30*time.Second,
    )
    defer cancel()
    agentID, err := session.Start(ctx)
    return agentID, session, err
}

生产代码要按 agentID 保存 AgentSession,停止时优先调 session.Stop(ctx);服务重启后 session 映射就没了,这时用 AgoraClient.StopAgent(ctx, agentID) 兜底。


六. Gin 三段式 API

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

type startRequest struct {
    ChannelName string `json:"channelName" binding:"required"`
    RTCUID      int    `json:"rtcUid" binding:"required,gt=0"`
    UserUID     int    `json:"userUid" binding:"required,gt=0"`
}

func addRoutes(router *gin.Engine, service *agentService) {
    router.GET("/get_config", getConfigHandler(service))
    router.POST("/startAgent", func(c *gin.Context) {
        var body startRequest
        if err := c.ShouldBindJSON(&body); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "detail": "请求参数无效",
            })
            return
        }
        agentID, err := service.start(
            body.ChannelName, body.RTCUID, body.UserUID,
        )
        if err != nil {
            c.JSON(http.StatusBadGateway, gin.H{
                "detail": "启动智能体失败",
            })
            return
        }
        c.JSON(http.StatusOK, gin.H{
            "code": 0,
            "data": gin.H{"agent_id": agentID},
        })
    })
}

完整可编译的 agent.gomain.go、测试和依赖文件都在 code/golang 目录里。


七. 运行与验证

cd code/golang
cp .env.example .env.local
go mod download
go test ./...
go run .

先试一下配置接口:

curl http://127.0.0.1:8000/get_config

预期返回 App ID、短期 Token、用户 UID、智能体 UID 和频道名。注意检查返回里没有 App Certificate 和模型 Key。这一眼值得多看两秒,密钥泄漏最常见的方式就是顺手多返回了一个字段。

然后让客户端加入这个频道,再调用:

curl -X POST http://127.0.0.1:8000/startAgent \
  -H 'Content-Type: application/json' \
  -d '{"channelName":"REPLACE_WITH_CHANNEL","rtcUid":1001,"userUid":2001}'

验收标准和 Python 快速开始一样:中文开场白、至少两轮语音对话、双方字幕、指标事件、正常停止、RTC/RTM 资源释放干净。


八. 故障排查

  • 请求发到了错误的接口地址:检查 import 路径里确实包含 /agentkit/cn,别和其他客户端混用。
  • MiniMax 构造器 panic:Key、Model、VoiceSetting.VoiceID 和音频设置四项都得传,少一个就会 panic。
  • 回复里出现思考内容:Params 里要传 thinking: {type: disabled}
  • Token 生成失败:检查 App ID、主要证书、频道名和 UID。再提醒一次,主要证书不能放前端。

九. 下一步

后端起来了,配一个前端就能开口说话——直接复用现成的客户端。

《Next.js 搭建浏览器端语音智能体》

在声网,连接无限可能

想进一步了解「对话式 AI 与 实时互动」?欢迎注册,开启探索之旅。

本博客为技术交流与平台行业信息分享平台,内容仅供交流参考,文章内容不代表本公司立场和观点,亦不构成任何出版或销售行为。