Appearance
Go SDK
官方 Go SDK,封装 AI 应用开发平台的 OpenAPI(/v1/openapi/*)。
安装
SDK 源码托管在内网私有 GitLab(gitlab.i.huaxisy.com),需要登录认证才能拉取。
1. 配置私有仓库认证
bash
# 设置 GOPRIVATE,跳过公共代理校验
go env -w GOPRIVATE=gitlab.i.huaxisy.com
# 配置 Git 认证(推荐 SSH 方式)
git config --global url."ssh://gitlab.i.huaxisy.com/".insteadOf "https://gitlab.i.huaxisy.com/"TIP
SSH 方式需要确保本机已配置 GitLab SSH Key。如使用 HTTPS 方式,可通过 ~/.netrc 或 Git 凭证管理器提供用户名/密码。
2. 安装 SDK
在 go.mod 中添加依赖并指定 replace:
go
require hxsyai.com/ai-adp-sdk/go v0.2.0
replace hxsyai.com/ai-adp-sdk/go v0.2.0 => gitlab.i.huaxisy.com/dail-tech/aiadp/sdk/sdk-go.git v0.2.0然后执行:
bash
go mod tidygo get 无法直接拉取私有仓库,必须通过 go.mod 中的 require + replace 指定。
要求 Go 1.24+。
快速开始
go
import (
aiadp "hxsyai.com/ai-adp-sdk/go"
"hxsyai.com/ai-adp-sdk/go/chat"
)
client, err := aiadp.NewClient(
"sk-xxx",
"https://runtime-gateway.invalid",
)
if err != nil {
panic(err)
}
chatCli := chat.New(client)
res, err := chatCli.SendMessagesBlocking(ctx, &chat.SendMessagesRequest{
AppID: "<your-app-id>",
UserID: "user-1",
Query: "你好",
})
fmt.Println(res.Output.Content)底层 aiadp.Client 仅负责通信。每个业务子包都用 New(client) 构造自己的客户端:
| 包 | 用途 |
|---|---|
apps | 应用列表 / 详情 / 运行参数 |
chat | Agent 对话(send-messages、resume、stop、会话 / 消息历史) |
files | Agent 工作空间文件操作 |
knowledge | 知识库列表 / 详情 / 检索 |
workflow | 工作流执行(blocking / streaming) |
thirdparty | 三方服务列表 / 工具列表 / 调用 |
voice | 语音合成 / 识别 / 音色列表(含 WebSocket 流式) |
event | SSE 事件强类型(chat / workflow) |
sse | 底层 SSE 行解析器 |
compatible | 模型发现、能力判断与 Rerank 平台扩展 |
compatible/openai | 构造已接入平台的 OpenAI 官方客户端 |
compatible/anthropic | 构造已接入平台的 Anthropic 官方客户端 |
NewClient 的第二个参数必须是网关根地址 https://runtime-gateway.invalid,不要追加 /v1/openapi 或 /v1/compatible-mode。SDK 会在内部生成业务 OpenAPI 与模型兼容接口地址,并在两类调用间共享 API Key、HTTP Client、超时、重试、日志和 User-Agent 配置。
配置
go
client, _ := aiadp.NewClient(apiKey, baseURL,
aiadp.WithTimeout(30*time.Second),
aiadp.WithStreamConnectTimeout(10*time.Second),
aiadp.WithRetry(3, time.Second),
aiadp.WithLogger(aiadp.NewSlogLogger(slog.Default())),
aiadp.WithUserAgent("my-service/1.0"),
aiadp.WithHTTPClient(myCustomClient),
)平台客户端还提供以下只读配置,供 compatible、WebSocket 或自定义子客户端复用:
go
client.BaseURL() // 网关根地址
client.OpenAPIBaseURL() // 业务 OpenAPI 地址
client.CompatibleBaseURL() // 模型兼容接口地址
client.APIKey()
client.HTTPClient()
client.Timeout()
client.StreamConnectTimeout()
client.MaxRetries()
client.UserAgent()
client.Logger()应用 apps
go
func (cli *Client) List(ctx, *ListRequest) (*ListResult, error)
func (cli *Client) Detail(ctx, *DetailRequest) (*DetailResult, error)
func (cli *Client) Parameters(ctx, *ParametersRequest) (*ParametersResult, error)go
cli := apps.New(client)
list, err := cli.List(ctx, &apps.ListRequest{Page: 1, PageSize: 20})
for _, a := range list.Record {
fmt.Printf("%s %s (type=%d)\n", a.ID, a.Name, a.AppType)
}
// 应用详情:含完整 app_schema(模型、提示词、工具、知识库、记忆、交互配置等)
detail, err := cli.Detail(ctx, &apps.DetailRequest{AppID: appID})
fmt.Printf("model=%s prologue=%s\n",
detail.AppSchema.ModelInfo.ModelID,
detail.AppSchema.InteractionConfig.ChatPrologue.Prologue)
// 运行参数:扁平化的交互配置(开场白、建议问题、语音 TTS/ASR、运行约束)
params, err := cli.Parameters(ctx, &apps.ParametersRequest{AppID: appID})
fmt.Printf("tts=%s asr=%s max_steps=%d\n",
params.Voice.TTS.Code, params.Voice.ASR.Provider, params.RuntimeSetting.MaxSteps)DetailResult.AppSchema 是应用的完整配置快照,ParametersResult 是面向运行时的精简交互配置;两者都支持 Version 指定历史版本。
对话 chat
go
type Client struct{ /* ... */ }
func New(c Doer) *Client
func (cli *Client) SendMessagesBlocking(ctx, *SendMessagesRequest) (*SendMessagesResult, error)
func (cli *Client) SendMessagesStream(ctx, *SendMessagesRequest) (*event.ChatStream, error)
func (cli *Client) ResumeBlocking(ctx, *ResumeRequest) (*SendMessagesResult, error)
func (cli *Client) ResumeStream(ctx, *ResumeRequest) (*event.ChatStream, error)
func (cli *Client) ReconnectStream(ctx, *ReconnectRequest) (*event.ChatStream, error)
func (cli *Client) Stop(ctx, taskID string) error
func (cli *Client) Conversations(ctx, *ConversationsRequest) (*ConversationsResult, error)
func (cli *Client) ChatHistory(ctx, *HistoryRequest) ([]HistoryItem, error)
func (cli *Client) SuggestQuestions(ctx, *SuggestQuestionsRequest) (*SuggestQuestionsResult, error)请求与响应字段:
go
type SendMessagesRequest struct {
AppID string `json:"app_id"`
UserID string `json:"user_id"`
Username string `json:"username,omitempty"`
ConversationID string `json:"conversation_id,omitempty"`
Query string `json:"query"`
Files []FileItem `json:"files,omitempty"`
Input map[string]any `json:"input,omitempty"`
WithTTS bool `json:"with_tts,omitempty"`
TTSFormat string `json:"tts_format,omitempty"`
SelectModelID string `json:"select_model_id,omitempty"`
}
type SendMessagesResult struct {
ConversationID string `json:"conversation_id"`
MessageID string `json:"message_id"`
TaskID string `json:"task_id"`
Status string `json:"status"`
Duration int64 `json:"duration"`
Output BlockingOutput `json:"output"` // Content / FinishReason / Tokens
}ChatFlow 应用还会在同一条对话流中返回以下强类型事件:
| 事件名 | Go 类型 | 主要数据 |
|---|---|---|
workflow.started | *event.WorkflowStarted | WorkflowRunID |
workflow.finished | *event.WorkflowFinished | 状态、输出、错误、耗时、Token 与步骤数 |
node.started | *event.NodeStarted | 节点执行 ID、节点类型、标题与输入 |
node.finished | *event.NodeFinished | 状态、输出、耗时与输入/输出 Token |
node.error | *event.NodeError | 节点错误、耗时与输入/输出 Token |
这些类型由 ChatStream.Next 和 NextWithID 自动分发;不需要自行解析 SSE event 字段。
response_mode由 SDK 自动注入,请勿自行设置。
流式响应
go
stream, err := chatCli.SendMessagesStream(ctx, req)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for {
ev, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Fatal(err)
}
switch e := ev.(type) {
case *event.Start:
case *event.Chunk:
fmt.Print(e.Data.Content)
case *event.Interrupt:
// HITL,见下方 Resume
case *event.Completed:
}
}HITL 中断与恢复
对话流出现 *event.Interrupt 时,会带回 CheckpointID 与 Interrupts[].InterruptID。按 interrupt ID 给出应答后调用 ResumeStream:
go
resume, err := chatCli.ResumeStream(ctx, &chat.ResumeRequest{
AppID: appID,
UserID: userID,
ConversationID: conversationID,
CheckpointID: checkpointID,
Responses: map[string]event.ResumeResponse{
interruptID: event.ChoiceResponse{Choice: "staging"},
},
})event.ResumeResponse 的实现类型对应不同交互:ChoiceResponse、ConfirmResponse、TextResponse 等。
断流重连
需要断点续传时,使用 NextWithID 保存服务端返回的 SSE ID,再通过 ReconnectStream 继续读取:
go
_, lastEventID, err := stream.NextWithID(ctx)
if err != nil {
log.Fatal(err)
}
reconnected, err := chatCli.ReconnectStream(ctx, &chat.ReconnectRequest{
AppID: appID,
UserID: userID,
TaskID: taskID,
LastEventID: lastEventID,
})
if err != nil {
log.Fatal(err)
}
defer reconnected.Close()同一个 ChatStream 只选择 Next、NextWithID 或 RawNext 中的一种读取方式,不要混用。
停止生成
从 *event.Start 拿到 TaskID 后随时可停:
go
if err := chatCli.Stop(ctx, taskID); err != nil {
log.Fatal(err)
}会话与消息历史
go
// 会话列表(按应用,可选按用户过滤、分页)
conv, err := chatCli.Conversations(ctx, &chat.ConversationsRequest{
AppID: appID, UserID: "demo-user", Page: 1, PageSize: 20,
})
for _, c := range conv.Record {
fmt.Printf("%s %s\n", c.ID, c.Name)
}
// 某会话的消息历史
items, err := chatCli.ChatHistory(ctx, &chat.HistoryRequest{
AppID: appID, ConversationID: conversationID, Page: 1, PageSize: 20,
})
for _, m := range items {
fmt.Printf("Q=%s A=%s\n", m.Query, m.Content)
}HistoryItem 含 Query / Content / Thinking / ToolCalls / Items(子 Agent 与工具调用明细)等完整执行轨迹。
建议问题
应用启用 UserSuggestion 后,可以根据某条回复生成后续建议问题:
go
result, err := chatCli.SuggestQuestions(ctx, &chat.SuggestQuestionsRequest{
AppID: appID,
MessageID: messageID,
})
if err != nil {
log.Fatal(err)
}
for _, question := range result.Questions {
fmt.Println(question)
}文件 files
go
func (cli *Client) Upload(ctx, *UploadRequest) (*FileObject, error)
func (cli *Client) ListDirectory(ctx, *ListRequest) (*ListResult, error)
func (cli *Client) ListTree(ctx, *TreeRequest) (*TreeResult, error)
func (cli *Client) Edit(ctx, *EditRequest) error
func (cli *Client) Copy(ctx, *CopyRequest) (*FileObject, error)
func (cli *Client) Move(ctx, *MoveRequest) (*FileObject, error)
func (cli *Client) Delete(ctx, *DeleteRequest) error
func (cli *Client) Mkdir(ctx, *MkdirRequest) (*FileObject, error)
func (cli *Client) Search(ctx, *SearchRequest) (*SearchResult, error)
func (cli *Client) Query(ctx, *QueryRequest) (*QueryResult, error)
func (cli *Client) Download(ctx, userID, filePath string) (*DownloadResult, error)
func (cli *Client) DownloadDir(ctx, userID, dirPath string) (*DownloadResult, error)
func (cli *Client) InitMultipartUpload(ctx, *InitMultipartUploadRequest) (*InitMultipartUploadResult, error)
func (cli *Client) UploadPart(ctx, *UploadPartRequest) (*MultipartPart, error)
func (cli *Client) CompleteMultipartUpload(ctx, *CompleteMultipartUploadRequest) (*FileObject, error)
func (cli *Client) MultipartUploadStatus(ctx, *MultipartUploadStatusRequest) (*MultipartUploadStatusResult, error)
func (cli *Client) AbortMultipartUpload(ctx, *AbortMultipartUploadRequest) error所有文件接口都需要 UserID 定位 Agent 工作空间。Upload 的 File 字段是任意 io.Reader:
go
cli := files.New(client)
obj, err := cli.Upload(ctx, &files.UploadRequest{
UserID: "demo-user",
Path: "/data/hello.txt",
Filename: "hello.txt",
File: strings.NewReader("hello world"),
})
fmt.Printf("uploaded: path=%s url=%s\n", obj.Path, obj.URL)
list, err := cli.ListDirectory(ctx, &files.ListRequest{UserID: "demo-user", Path: "/data"})
for _, f := range list.Files {
fmt.Printf(" - %s (size=%d)\n", f.Path, f.Size)
}Move 对应网关的 rename 接口,同目录即重命名、跨目录即移动。ListTree 递归列出文件树,MaxDepth 为 0 表示不限深度。Query 可按路径批量查询文件并返回完整访问 URL;大文件上传使用 init → part → complete 流程,并可查询状态或中止任务。
知识库 knowledge
go
func (cli *Client) List(ctx, *ListRequest) (*ListResult, error)
func (cli *Client) Detail(ctx, knowledgeID string) (*Detail, error)
func (cli *Client) Retrieve(ctx, knowledgeID string, *RetrieveRequest) (*RetrieveResult, error)go
cli := knowledge.New(client)
list, err := cli.List(ctx, &knowledge.ListRequest{Page: 1, PageSize: 10})
first := list.Items[0]
detail, err := cli.Detail(ctx, first.ID)
fmt.Printf("retriever_type=%d top_k=%d\n",
detail.RetrieverConfig.RetrieverType, detail.RetrieverConfig.TopK)
res, err := cli.Retrieve(ctx, first.ID, &knowledge.RetrieveRequest{
Query: "高血压患者能不能吃布洛芬?",
})
for _, r := range res.Records {
fmt.Printf("- score=%.3f content=%s\n", r.Score, r.Content)
}可选地用 RetrievalModel 覆盖单次检索的 TopK / ScoreThreshold 等参数。
注意类型差异:详情响应里
ScoreThresholdEnabled是int(0/1),而检索请求体里的同名字段是bool。
工作流 workflow
go
func (cli *Client) InvokeBlocking(ctx, *InvokeRequest) (*InvokeResult, error)
func (cli *Client) InvokeStream(ctx, *InvokeRequest) (*event.WorkflowStream, error)go
cli := workflow.New(client)
res, err := cli.InvokeBlocking(ctx, &workflow.InvokeRequest{
WorkflowID: workflowID,
Input: map[string]any{"input": "华西"},
})
fmt.Printf("final_output=%v token=%d\n", res.FinalOutput, res.Token)
stream, err := cli.InvokeStream(ctx, &workflow.InvokeRequest{
WorkflowID: workflowID,
Input: map[string]any{"input": "华西"},
})
defer stream.Close()
for {
ev, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
switch e := ev.(type) {
case *event.WorkflowMessage:
fmt.Print(e.Content)
case *event.WorkflowDone:
fmt.Printf("\n[done] status=%s\n", e.Status)
case *event.WorkflowError:
fmt.Printf("\n[error] %s\n", e.ErrorMessage)
}
}三方服务 thirdparty
go
func (cli *Client) ListServices(ctx) ([]Service, error)
func (cli *Client) ListTools(ctx, serviceSlug string) (*ToolsResult, error)
func (cli *Client) Invoke(ctx, serviceSlug, toolSlug string, *InvokeRequest) (*InvokeResult, error)go
cli := thirdparty.New(client)
services, err := cli.ListServices(ctx)
for _, s := range services {
fmt.Printf("- %s (configured=%v)\n", s.Name, s.IsConfigured)
}
tools, err := cli.ListTools(ctx, services[0].Name)
fmt.Printf("service=%s tools=%d\n", tools.Service.Name, len(tools.Tools))
res, err := cli.Invoke(ctx, services[0].Name, tools.Tools[0].Name,
&thirdparty.InvokeRequest{Inputs: map[string]any{}})
fmt.Printf("output=%s\n", string(res.Output)) // Output 是 json.RawMessage不同工具返回结构不同,InvokeResult.Output 是 json.RawMessage,按需自行反序列化。
语音 voice
go
// 非流式
func (cli *Client) Synthesize(ctx, *SynthesizeRequest) (*TTSResult, error)
func (cli *Client) Recognize(ctx, *RecognizeRequest) (*STTResult, error)
func (cli *Client) ListVoices(ctx, provider string) ([]VoiceOption, error)
// WebSocket 流式
func (cli *Client) TTSStream(ctx, *SynthesizeRequest) (*TTSStream, error)
func (cli *Client) STTStream(ctx, *RecognizeRequest) (*STTStream, error)音频在请求/响应中以 base64 传输(AudioData 字段)。语音合成:
go
cli := voice.New(client)
voices, err := cli.ListVoices(ctx, "aliyun")
for _, v := range voices {
fmt.Printf("- %s (%s) %s/%s\n", v.Name, v.Value, v.Language, v.Scene)
}
res, err := cli.Synthesize(ctx, &voice.SynthesizeRequest{
Provider: "aliyun",
Text: "你好,欢迎使用语音合成。",
VoiceCode: voices[0].Value,
Format: "mp3",
})
audio, _ := base64.StdEncoding.DecodeString(res.AudioData)
fmt.Printf("format=%s duration=%dms bytes=%d\n", res.Format, res.DurationMs, len(audio))语音识别(非流式):
go
res, err := cli.Recognize(ctx, &voice.RecognizeRequest{
Provider: "aliyun",
AudioData: base64.StdEncoding.EncodeToString(data),
Format: "pcm",
SampleRate: 16000,
})
fmt.Printf("text=%q confidence=%.2f\n", res.Text, res.Confidence)WebSocket 流式
- TTS:
TTSStream.Recv()循环读取音频分片,io.EOF结束。 - STT:
STTStream.SendAudio(data)边推送音频边由Recv()接收*STTStreamResult(含IsPartial/IsFinal),推送完调CloseSend()。
两个流都实现 Close(),记得 defer 释放连接。STT 流式时 RecognizeRequest 的非音频字段作为首帧配置,不要带 AudioData。
模型服务 compatible
Go SDK 可以从同一个平台客户端构造 OpenAI、Anthropic 官方客户端,以及模型发现和 Rerank 扩展客户端:
go
import (
aiadp "hxsyai.com/ai-adp-sdk/go"
"hxsyai.com/ai-adp-sdk/go/compatible"
compatibleanthropic "hxsyai.com/ai-adp-sdk/go/compatible/anthropic"
compatibleopenai "hxsyai.com/ai-adp-sdk/go/compatible/openai"
)
platform, err := aiadp.NewClient(apiKey, "https://runtime-gateway.invalid")
if err != nil {
log.Fatal(err)
}
openaiClient, err := compatibleopenai.New(platform)
if err != nil {
log.Fatal(err)
}
anthropicClient, err := compatibleanthropic.New(platform)
if err != nil {
log.Fatal(err)
}
modelClient, err := compatible.New(platform)
if err != nil {
log.Fatal(err)
}| 客户端 | 用途 | 返回类型与错误 |
|---|---|---|
compatible/openai | Chat Completions、Responses、Embedding、语音等 OpenAI 兼容能力 | OpenAI 官方 Go SDK 类型与官方错误 |
compatible/anthropic | Messages、流式 Messages、Token 计数 | Anthropic 官方 Go SDK 类型与官方错误 |
compatible | 模型列表、模型详情、能力判断、Rerank | AI ADP 扩展类型;服务错误为 *compatible.Error |
三个客户端自动复用平台客户端的 API Key、HTTP Client、超时、重试、日志和 User-Agent。标准协议的调用方式与官方 SDK 一致,平台地址无需再次配置。
模型发现与能力判断
go
models, err := modelClient.ListModels(ctx)
if err != nil {
log.Fatal(err)
}
for _, model := range models.Data {
if model.HasCapability("chat_completions") {
fmt.Println(model.ID)
}
}
model, err := modelClient.GetModel(ctx, modelID)
if err != nil {
log.Fatal(err)
}模型 ID 可以包含提供商前缀,例如 provider/model-name;直接传入模型列表返回的 Model.ID,不要自行拼接。
Rerank
go
topN := 2
result, err := modelClient.Rerank(ctx, &compatible.RerankRequest{
Model: modelID,
Query: "什么是向量检索?",
Documents: []compatible.RerankDocument{
compatible.StringDocument("向量检索通过相似度召回内容。"),
compatible.ObjectDocument("doc-2", "关系型数据库使用表组织数据。"),
},
TopN: &topN,
})
if err != nil {
log.Fatal(err)
}RerankDocument 同时支持字符串和带 ID 的对象;应使用 StringDocument 或 ObjectDocument 构造,空文档会返回 compatible.ErrInvalidRerankDocument。完整协议能力参见模型服务说明。
错误处理
go
if errors.Is(err, aiadp.ErrAPIKeyExpired) {
// 重新生成 API Key
}
var apiErr *aiadp.APIError
if errors.As(err, &apiErr) {
log.Printf("status=%d code=%d message=%s trace=%s",
apiErr.HTTPStatus, apiErr.Code, apiErr.Message, apiErr.TraceID)
}模型发现与 Rerank 的协议错误可读取结构化字段:
go
var compatibleErr *compatible.Error
if errors.As(err, &compatibleErr) {
log.Printf("status=%d type=%s code=%s request_id=%s",
compatibleErr.HTTPStatus,
compatibleErr.Type,
compatibleErr.Code,
compatibleErr.RequestID,
)
}OpenAI 与 Anthropic 标准客户端保留各自官方 SDK 的错误类型,不会转换为 aiadp.APIError 或 compatible.Error。
错误码与文案以网关/服务实际返回为准,详见 错误处理。
示例
完整可运行示例见 SDK 仓库的 examples/,每个目录是一个独立 main:
| 示例 | 功能 |
|---|---|
apps-list / apps-detail / apps-parameters | 应用列表 / 详情 / 运行参数 |
chat-blocking / chat-streaming | 对话两种模式 |
chat-resume | HITL 中断 + resume(choice) |
chat-stop | task-id 停止 |
chat-history | 会话列表 + 消息历史 |
files-upload / files-download / files-tree | 普通与分片上传 / 文件与目录下载 / 文件树 |
knowledge-retrieve | list / detail / retrieve |
workflow-invoke | blocking + streaming |
thirdparty-invoke | services / tools / invoke |
voice-tts / voice-stt | 语音合成 / 识别(含流式) |
compatible-openai | OpenAI Responses 流式输出思考与回答 |
compatible-anthropic | Anthropic Messages 流式输出与 Token 计数 |
compatible-rerank | 模型能力判断与 Rerank |
运行前设置环境变量:
bash
export AIADP_API_KEY=sk-xxx
export AIADP_BASE_URL=https://runtime-gateway.invalid
export AIADP_MODEL_ID=your-model-id
go run ./examples/chat-streaming只有兼容模型示例需要 AIADP_MODEL_ID;其他示例按各自 README 设置应用、用户、知识库或工作流等业务参数。
