跳到主要内容

AI 契约

ai 包定义了与 provider 无关的 chat model、tool、agent、message 和 stream 契约。框架默认不内置具体模型 provider;应用或 provider 包通过公开 registry 注册自己的实现。

Messages

type Message struct {
Role Role
Content string
ToolCalls []ToolCall
ToolResult *ToolResult
Usage *TokenUsage
}

Role 公开为常量:RoleSystemRoleUserRoleAssistantRoleTool。 常见消息可用 helper 构造:

messages := []*ai.Message{
ai.NewSystemMessage("Answer briefly."),
ai.NewUserMessage("Summarize this invoice."),
}

Assistant tool request 使用 NewAssistantMessageWithToolCalls(...),tool result 使用 NewToolMessage(callID, content);普通 assistant 回复用 NewAssistantMessage(...)Message.IsSystemIsUserIsAssistantIsToolHasToolCalls 是给路由与测试使用的便捷谓词。

helper 构造函数会精确填充这些字段:

Helper填充字段
NewSystemMessage(content)Role: RoleSystem, Content: content
NewUserMessage(content)Role: RoleUser, Content: content
NewAssistantMessage(content)Role: RoleAssistant, Content: content
NewAssistantMessageWithToolCalls(content, toolCalls)Role: RoleAssistant, Content: content, ToolCalls: toolCalls
NewToolMessage(callID, content)Role: RoleTool, ToolResult: &ToolResult{CallID: callID, Content: content};顶层 Message.Content 不会被填充

Models

type ChatModel interface {
Generate(ctx context.Context, messages []*Message, opts ...Option) (*Message, error)
Stream(ctx context.Context, messages []*Message, opts ...Option) (MessageStream, error)
}

type ToolableChatModel interface {
ChatModel
WithTools(tools ...Tool) ToolableChatModel
}

WithTools 使用不可变模式:它返回一个绑定了 tools 的 model instance,不会 修改当前 instance。

运行时 options 是 functional options:

reply, err := model.Generate(ctx, messages,
ai.WithTemperature(0.2),
ai.WithMaxTokens(512),
ai.WithStopSequences("\n\n"),
ai.WithMeta("trace_id", traceID),
)

NewOptions() 会创建默认 option 累加器;Options.Apply(...) 把 functional options 折叠进去。大多数应用代码直接把 options 传给 GenerateStreamRunInvoke,不需要手工操作累加器。 Apply 会修改并返回同一个累加器,按参数顺序应用 options;后传入的 option 可以覆盖之前 option 设置的字段。

运行时 option 字段与 helper:

字段或 helper契约
Options.Temperature / WithTemperature(t)可选 temperature pointer,值为 t
Options.MaxTokens / WithMaxTokens(n)可选 maximum-token pointer,值为 n
Options.StopSequences / WithStopSequences(seqs...)stop sequence slice 会被替换为 seqs
Options.Meta / WithMeta(key, value)string metadata map;NewOptions() 会初始化它,WithMeta 在需要时也会创建它

Model metadata 和 configuration 字段:

Struct公开字段
ModelConfigProvider, Model, APIKey, BaseURL, Temperature, MaxTokens, Timeout
ModelInfoProvider, Model, MaxTokens, Temperature

Provider Registry

模型 provider 实现:

type ModelProvider interface {
Name() string
CreateModel(ctx context.Context, cfg *ModelConfig) (ToolableChatModel, error)
}

在应用启动或 package initialization 阶段注册一次:

ai.RegisterModelProvider(openaiProvider)

model, err := ai.NewChatModel(ctx, &ai.ModelConfig{
Provider: "openai",
Model: "gpt-4.1-mini",
APIKey: apiKey,
})

RegisterModelProvider 遇到重复名称会 panic,消息为 ai: model provider "{name}" already registeredRegisterAgentFactory 使用 ai: agent factory "{name}" already registeredNewChatModelcfg.Provider 没有对应 provider 时返回带有请求 provider 名称的 ai.ErrProviderNotFound wrapped error。NewAgentBuilder 也会用 ai.ErrAgentNotFound 和请求的 agent type 做同样处理。cfg、provider instance 和 factory instance 都是非 nil 前置条件;registry 在调用 Name() 或 读取 cfg.Provider 前不会额外做 nil guard。

Agent factory 走同样模式:RegisterAgentFactory(...)NewAgentBuilder(...)ListAgentFactories()ai.ErrAgentNotFoundListModelProviders() 可用于查看已注册的模型 provider 名称。registry list helpers 返回 map keys,不定义稳定排序。

Factory 实现会交换 ModelConfigModelInfoAgentConfigAgentFactory。这些都是给 provider package 使用的普通公开 struct / interface;应用通常只在注册自己的 model 或 agent 实现时直接接触它们。

Tools

Tool 暴露 JSON-schema-style metadata 和 invocation 方法:

type Tool interface {
Info() *ToolInfo
Invoke(ctx context.Context, arguments string) (string, error)
}

arguments 是模型传来的 JSON-encoded string。Tool 实现应在工具边界解码并 校验它,再返回给模型使用的 string payload。

Tool metadata 使用 ToolInfoParameterSchemaPropertySchema。schema 形态刻意保持很小:typepropertiesrequireddescriptionenumitems 覆盖 provider tool calling 需要的 JSON-schema 子集。

Tool 相关数据形状:

Struct公开字段
ToolCallID, Name, Arguments
ToolResultCallID, Content
ToolInfoName, Description, Parameters
ParameterSchemaType, Properties, Required
PropertySchemaType, Description, Enum, Items

支持流式输出的 tool 实现 StreamableTool

type StreamableTool interface {
Tool
InvokeStream(ctx context.Context, arguments string) (StringStream, error)
}

Agents

Agent 是更高层的 runner,可以用 model 和 tools 完成推理:

type Agent interface {
Run(ctx context.Context, input string, opts ...Option) (*Message, error)
Stream(ctx context.Context, input string, opts ...Option) (MessageStream, error)
}

AgentBuilder 让已注册 factory 暴露 fluent setup:

builder, err := ai.NewAgentBuilder("tool-loop")
if err != nil {
return err
}

agent, err := builder.
WithModel(model).
WithTools(searchTool, calculatorTool).
WithSystemPrompt("Use tools when needed.").
WithMaxIterations(8).
Build(ctx)

AgentConfig 暴露同一组 builder 输入字段:ModelToolsSystemPromptMaxIterations

Streams

base package 定义了两种 stream contract:

type MessageStream interface {
io.Closer
Recv() (*MessageChunk, error)
Collect() (*Message, error)
}

type StringStream interface {
io.Closer
Recv() (string, error)
Collect() (string, error)
}

Recv 在 stream 耗尽时返回 io.EOF

Stream 数据形状:

Struct公开字段
MessageChunkContent, ToolCalls, Done
TokenUsagePromptTokens, CompletionTokens, TotalTokens

UI Message SSE Streams

ai/stream 包会把消息转换成兼容 AI SDK UI Message Stream protocol 的 Server-Sent Events。它设置标准 SSE headers,输出 data: ... frame,并以 data: [DONE] 结束。

常见 adapter:

HelperSource
stream.FromChannel(ch)stream.Message 的 Go channel
stream.FromCallback(fn)通过 CallbackWriter 写入的 callback
stream.FromAiMessageStream(s)一个 ai.MessageStream
stream.NewChannelSource(ch)channel 的底层 MessageSource adapter
stream.NewCallbackSource(fn)callback 的底层 MessageSource
stream.NewAiMessageStreamSource(s)ai.MessageStream 的底层 adapter

内置 adapter 行为:

Adapter行为
NewChannelSource(ch)Recv() 会一直返回 channel message,直到 channel 关闭后返回 io.EOFClose() 只把 source 标记为已关闭,后续 Recv() 返回 io.EOF,不会关闭调用方持有的 channel
NewCallbackSource(fn)在 goroutine 中运行 fn,并使用带缓冲的 message channel;已排队的消息会先交付,然后才返回 callback error;Close() 会等待 callback goroutine 结束
NewAiMessageStreamSource(s)把每个 ai.MessageChunk 转成 assistant stream.Message,转发 Content 和 tool calls;Close() 委托给被包装的 ai.MessageStream

Fiber handler 示例:

import aistream "github.com/coldsmirk/vef-framework-go/ai/stream"

func Chat(ctx fiber.Ctx) error {
return aistream.FromCallback(func(w aistream.CallbackWriter) error {
w.WriteText("Hello")
w.WriteText(" from VEF")
w.WriteData("usage", map[string]int{"totalTokens": 12})

return nil
}).Stream(ctx)
}

Builder 默认输出 start/finish chunks;当 source 提供对应 message parts 时, 还会输出 text、reasoning、tool input、tool output 和自定义 data-{type} chunks。source 与 file chunks 通过低层构造函数开放给自定义 writer; WithSources(bool) 目前只是切换公开 option 字段,不会让普通 MessageSource 自动输出 sources。 WithReasoning(bool) 会控制 reasoning 输出,而且只有传入的 stream.Message.Reasoning 非空时才会输出 reasoning chunks。

默认 stream options:

Option默认值
SendReasoningtrue
SendSourcestrue
SendStarttrue
SendFinishtrue
OnError返回 err.Error()
OnFinishnil
GenerateIDDefaultOptions() 中为 nil;builder fallback 为 prefix + "_" + id.GenerateUUID()

Builder 控制项:

方法作用
WithSource(source)设置自定义 MessageSource
WithMessageID(id) / WithIDGenerator(fn)控制生成的 chunk ID
WithStart(bool) / WithFinish(bool)开关 start/finish chunk
WithReasoning(bool) / WithSources(bool)开关 reasoning 输出与公开的 source-output option
WithHeader(key, value)增加 SSE response header
OnError(fn) / OnFinish(fn)自定义错误文本或完成回调
Stream(ctx) / StreamToWriter(w)写入 Fiber 或 buffered writer

执行路径契约:

路径行为
Stream(ctx) 未配置 WithSource(...)写 headers 前返回 stream.ErrSourceRequired
Stream(ctx) 已配置 source先写 SseHeaders,再写 WithHeader 的覆盖/新增 header,然后通过 fiber.Ctx.SendStreamWriter 输出
StreamToWriter(w)直接写入 buffered writer,必须先配置 source;不同于 Stream(ctx),它没有缺失 source 的 guard
source lifecyclestream loop 退出时会关闭已配置的 source
source 正常返回 io.EOF关闭已打开的 text/reasoning chunks;当 SendFinish 为 true 时写 finish-stepfinish;写 data: [DONE];配置了 OnFinish 时调用 OnFinish(fullContent)
禁用 start/finishWithStart(false) 会同时省略 startstart-stepWithFinish(false) 会同时省略 finish-stepfinish,但仍然写 data: [DONE]
source 返回非 EOF 错误OnError(err)error chunk,写 data: [DONE],不会调用 OnFinish;已打开的 text/reasoning chunks 不会先被显式结束
OnFinish(fullContent)只收到拼接后的 Content text chunks,不包含 reasoning、tool payload 或自定义 data
tool call argumentsJSON-unmarshal 后作为 input payload;非法 JSON 会按原始 string 输出;空 tool call ID 会被替换为生成的 call_* ID
tool result contentJSON-unmarshal 后作为 output payload;非法 JSON 会按原始 string 输出
自定义 data messagesData map 的每个 entry 会输出一个 data-{type} chunk;同一条 message 包含多个 data key 时,map iteration order 不稳定
writer errorsstream loop 内部的 chunk/write error 会被忽略,因为 StreamToWriter 和 Fiber stream callback 不会从 loop 返回 error

默认 SSE headers:

HeaderValue
Content-Typetext/event-stream
Cache-Controlno-cache
Connectionkeep-alive
Transfer-Encodingchunked
X-Vercel-AI-UI-Message-Streamv1
X-Accel-Bufferingno

低层 chunk 构造函数也是公开 API,适合测试和自定义 writer 使用:

Chunk 家族构造函数
生命周期NewStartChunk, NewFinishChunk, NewStartStepChunk, NewFinishStepChunk, NewErrorChunk
textNewTextStartChunk, NewTextDeltaChunk, NewTextEndChunk
reasoningNewReasoningStartChunk, NewReasoningDeltaChunk, NewReasoningEndChunk
toolsNewToolInputStartChunk, NewToolInputDeltaChunk, NewToolInputAvailableChunk, NewToolOutputAvailableChunk
sources/files/dataNewSourceURLChunk, NewSourceDocumentChunk, NewFileChunk, NewDataChunk

精确 chunk wire shapes:

构造函数Wire type字段
NewStartChunk(messageID)starttype, messageID
NewFinishChunk()finishtype
NewStartStepChunk()start-steptype
NewFinishStepChunk()finish-steptype
NewErrorChunk(errorText)errortype, errorText
NewTextStartChunk(id)text-starttype, id
NewTextDeltaChunk(id, delta)text-deltatype, id, delta
NewTextEndChunk(id)text-endtype, id
NewReasoningStartChunk(id)reasoning-starttype, id
NewReasoningDeltaChunk(id, delta)reasoning-deltatype, id, delta
NewReasoningEndChunk(id)reasoning-endtype, id
NewToolInputStartChunk(toolCallID, toolName)tool-input-starttype, toolCallID, toolName
NewToolInputDeltaChunk(toolCallID, delta)tool-input-deltatype, toolCallID, inputTextDelta
NewToolInputAvailableChunk(toolCallID, toolName, input)tool-input-availabletype, toolCallID, toolName, input
NewToolOutputAvailableChunk(toolCallID, output)tool-output-availabletype, toolCallID, output
NewSourceURLChunk(sourceID, url, title)source-urltype, sourceID, urltitle 仅在非空时出现
NewSourceDocumentChunk(sourceID, mediaType, title)source-documenttype, sourceID, mediaTypetitle 仅在非空时出现
NewFileChunk(fileID, mediaType, url)filetype, fileID, mediaType, url
NewDataChunk(dataType, data)data-{dataType}type, datadata-{dataType} 是动态值,不是 ChunkType 常量

CallbackWriter 写入这些 source messages:

方法Message 效果
WriteText(content)Content 的 assistant message
WriteToolCall(id, name, arguments)带一个 ToolCall 的 assistant message
WriteToolResult(toolCallID, content)ToolCallIDContent 的 tool message
WriteReasoning(reasoning)Reasoning 的 assistant message
WriteData(dataType, data)Data[dataType] 的 assistant message
WriteMessage(msg)原样转发传入的 stream.Message

stream package 数据形状:

Struct 或 interface公开字段或方法
stream.MessageRole, Content, ToolCalls, ToolCallID, Reasoning, Data
stream.ToolCallID, Name, Arguments
stream.SourceType, ID, URL, Title, MediaType
stream.OptionsSendReasoning, SendSources, SendStart, SendFinish, OnError, OnFinish, GenerateID
MessageSourceRecv(), Close()Recv() 完成时返回 io.EOF
StreamWriterWriteChunk(chunk), Flush()
ResponseWriter与 Fiber stream writers 兼容的 io.Writer

stream.SseHeaders 包含默认 AI SDK UI Message Stream headers。 builder 缺少 source 时返回 stream.ErrSourceRequiredstream.ErrSourceClosed 是留给自定义 MessageSource 实现使用的公开 sentinel;内置 channel 和 callback adapter 正常结束时返回 io.EOF

其他 stream 公开 API:

API 组公开 surface
role 常量stream.RoleUser, stream.RoleAssistant, stream.RoleTool, stream.RoleSystem
chunk type 常量ChunkType, ChunkTypeStart, ChunkTypeFinish, ChunkTypeStartStep, ChunkTypeFinishStep, ChunkTypeError, ChunkTypeTextStart, ChunkTypeTextDelta, ChunkTypeTextEnd, ChunkTypeReasoningStart, ChunkTypeReasoningDelta, ChunkTypeReasoningEnd, ChunkTypeToolInputStart, ChunkTypeToolInputDelta, ChunkTypeToolInputAvailable, ChunkTypeToolOutputAvailable, ChunkTypeSourceURL, ChunkTypeSourceDocument, ChunkTypeFile
stream 数据结构stream.Message, stream.ToolCall, stream.Source, stream.Chunk, stream.Options
writer/source interfaceCallbackWriter, MessageSource, ResponseWriter, StreamWriter
默认值与构造器stream.New(), DefaultOptions(), SseHeaders

Error Sentinels

Error含义
ai.ErrProviderNotFound没有注册与 ModelConfig.Provider 匹配的 model provider
ai.ErrModelNotSupportedprovider 不支持请求的 model
ai.ErrAgentNotFound没有注册与请求类型匹配的 agent factory
ai.ErrToolNotFoundagent/model 请求了不可用的 tool
ai.ErrInvalidArgumentstool arguments 校验失败
ai.ErrMaxIterationsReachedagent 超过最大迭代次数
ai.ErrNoContentmodel 没有返回可用内容
ai.ErrStreamClosedstream 关闭后继续读取
stream.ErrSourceRequiredai/stream builder 没有 message source
stream.ErrSourceClosed给自定义 MessageSource 实现使用的公开 sentinel

NewToolError(...)NewModelError(...) 用于创建结构化的 ToolError / ModelErrorToolError.Unwrap() 会暴露底层 tool error。

结构化 error 形状:

Type公开字段与行为
ToolErrorToolName, ErrError() 返回 ai: tool {ToolName}: {Err}Unwrap() 返回 Err
ModelErrorProvider, StatusCode, MessageError() 返回 ai: {Provider}: {Message}