ack

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2026 License: MIT Imports: 15 Imported by: 0

README

ack — trpc-agent-go 的工具调用许可库

ack 是构建在 trpc-agent-go AG-UI 协议栈之上的 Go 公共库,为 Agent 应用提供工具调用许可(tool permission)能力:

  • 层级分类:工具按 allow(默认放行)/ confirm(需人工确认)/ deny(禁用)分级;
  • 策略源:外部裁决系统(如风控)在每次调用时介入,可强制确认、背书降级或直接拒绝;
  • 许可:用户预先授予的授权(会话级/全局级)自动满足确认要求,决定时勾选"记住"即可生成;
  • 人工确认全生命周期:运行存活时阻塞唤醒(live 路径),服务重启后孤儿确认经会话历史软恢复续跑(recovery 路径);
  • 授权终态观察:全部自动放行/拒绝终态经回调外报宿主审计,人工决定归宿主 Decide 路由;
  • HTTP 绑定:确认决定/查询路由可挂载到任意 http.ServeMux。

完整设计(评估管线、事件契约、不变量、部署约束)见 docs/module_design.md;接入步骤、配置项与前端对接见 docs/usage.md;实现原理与机制图解见 docs/mechanism.md。

仓库布局

ack.go / permission.go / policy.go / confirm.go / recovery.go / grants.go / payload.go / errors.go / doc.go
  核心库(包 ack)
ackhttp/   确认决定/查询的 HTTP 路由(仅依赖标准库与 ack)
acktest/   测试夹具:脚本化模型、假续跑 runner
examples/notes/   端到端示例:笔记助手(Go 后端 + Vue3 前端 ack_notes/)
docs/module_design.md   设计文档(唯一设计事实来源)
docs/usage.md   使用文档(接入步骤、配置项、HTTP API、前端对接、测试)
docs/mechanism.md   原理机制文档(实现视角,含图解)

快速开始(示例)

# 1. 构建前端(产物不入库,仓库内有占位页)
cd examples/notes/ack_notes
pnpm install && pnpm build

# 2. 运行示例(离线 mock 模型,内存会话)
cd ../../..
go run ./examples/notes
# 打开 http://127.0.0.1:8080

# 以 MCP 工具模式运行:自动启动 MCP server 并对接 agent
#(启动日志打印 MCP 端点,外部 MCP 客户端可直连观察)
go run ./examples/notes -mcp

默认脚本演示完整许可闭环:读取 n1(allow 直行)→ 删除 n2(确认卡片)→ 删除 n3(勾选"记住授权"后免确认)→ 总结。

示例环境变量
变量 默认 说明
NOTES_ADDR 127.0.0.1:8080 HTTP 监听地址
NOTES_SESSION_BACKEND memory memory / postgres(重启恢复演示需 postgres)
NOTES_PG_DSN 本机默认 PostgreSQL 连接串(库不存在时自动创建)
NOTES_LLM deepseek mock(脚本化模型,离线)或真实提供方
NOTES_API_KEY 空 为空自动回退 mock;也读 DEEPSEEK_API_KEY
NOTES_BASE_URL / NOTES_MODEL DeepSeek OpenAI 兼容接口与模型名
NOTES_RISK_POLICY 0 置 1 启用 mock 风控策略源(演示强制确认/背书/deny 三形态)

命令标记:-mcp 启用 MCP 工具模式——自动启动 MCP server(streamable HTTP)并把同一批笔记工具原样接入 agent,验证 ack 对 MCP 工具同管线生效;默认 function 模式(进程内直接调用)。

示例端点
路由 说明
/ 前端(pnpm build 后嵌入单二进制;开发用 pnpm dev 代理)
POST /agui / POST /agui/history / POST /agui/cancel AG-UI SSE
POST /ack/confirmation 确认决定({threadId, confirmationId, approved, remember?},返回 {path: live|recovery})
GET /ack/confirmations?threadId= 待确认查询(含孤儿,live=false)
GET /api/sessions / DELETE /api/sessions/{id} 会话列表/删除(示例级参考实现)

库接入

gw := ack.New(svc,
    ack.WithApp("biz"),                    // 须与 AG-UI runner 的 AppName 一致
    ack.WithUser("u1"),
    ack.WithTiers(map[string]ack.Tier{"delete_note": ack.TierConfirm}),
    ack.WithPolicies(riskControl),         // 可选:策略源链
    ack.WithGrants(ack.MemoryGrants()),    // 业务可换自有存储
    ack.WithStaticTools(readNote, deleteNote),
    ack.WithPermissionObserver(func(ctx context.Context, e ack.PermissionEvent) {
        log.Println("permission settled", e.Call.ToolName, e.Basis) // 可选:终态外报
    }),
)

r := aguirunner.New(coreRunner,
    aguirunner.WithAppName("biz"),
    aguirunner.WithSessionService(svc),
    aguirunner.WithRunOptionResolver(gw.RunOptionResolver()),
    aguirunner.WithTimeout(10*time.Minute), // 未决确认最终由超时结算为拒绝
)
_ = gw.Bind(r)

ackhttp.Mount(mux, gw) // POST /ack/confirmation、GET /ack/confirmations

前端约定:消费 tool_confirmation 自定义事件渲染确认卡片,决定时调用 POST /ack/confirmation(可携带 remember),重连时以待确认查询补偿孤儿状态,续跑结果经 /agui/history follow 流读取。事件与存储契约见设计文档 §8。

依赖注记:trpc-mcp-go fork

上游 trpc.group/trpc-go/trpc-mcp-go 截至 v0.0.19 的 streamable 客户端存在会话状态并发访问缺陷:GET SSE goroutine 与 POST 响应处理竞争 sessionID / lastEventID,-race 下偶发数据竞争。修复位于 fork github.com/JNinng/trpc-mcp-go(tag 为「上游基线版本 + -N 修补序号」)。

Go 的 replace 只在主模块生效,本库 go.mod 中的 replace 不会被消费方继承,因此每个主模块需自行声明:

go mod edit -replace trpc.group/trpc-go/trpc-mcp-go=github.com/JNinng/trpc-mcp-go@v0.0.19-3

上游发布含该修复的版本后,删除此 replace 并升级依赖即可。

测试

go test -race -count=1 ./...          # 库 + ackhttp + 示例集成测试
cd examples/notes/ack_notes && pnpm exec vitest run   # 前端单测

Go 集成测试对恢复路径的断言采用"快照轮询"编写,不依赖 history follow 流的自然终止(见设计文档 D4 对上游时间戳缺陷的说明)。

许可

MIT

Documentation

Overview

Package ack 在 trpc-agent-go 的 AG-UI 协议栈之上提供工具调用许可 (tool permission)能力。

工具按层级(Tier)分类:默认放行(allow)、需人工确认(confirm)、 禁用(deny)。每次工具调用先经许可评估:外部策略源(PolicySource, 如风控)可介入裁决,用户预先授予的许可(Grant,会话级/全局级)可 满足确认要求,仅当无任何放行依据时才阻塞等待人工决定。全部自动 授权终态可经 WithPermissionObserver 外报宿主审计;人工决定不通知, 归宿主 Decide 路由。

人工确认的完整生命周期由网关(Gateway)承载:

  • 直接路径(live):发出确认的运行存活时,决定唤醒阻塞的运行 继续执行,事件仍走原始 SSE/follow 流;
  • 恢复路径(recovery):运行已不存在(如服务重启)时,由网关 代执行工具或合成拒绝结果,并拉起一次续跑运行,前端经 /history follow 流读取。

前端经 AG-UI 自定义事件 tool_confirmation 渲染确认卡片,经 ackhttp.Mount 挂载的 HTTP 路由提交决定。最小接入:

gw := ack.New(svc,
	ack.WithApp("biz"), // 须与 AG-UI runner 的 AppName 一致
	ack.WithTiers(map[string]ack.Tier{"delete_note": ack.TierConfirm}),
	ack.WithGrants(ack.MemoryGrants()),
	ack.WithStaticTools(readNote, deleteNote),
)
r := aguirunner.New(coreRunner,
	aguirunner.WithAppName("biz"),
	aguirunner.WithSessionService(svc),
	aguirunner.WithRunOptionResolver(gw.RunOptionResolver()),
)
_ = gw.Bind(r)
ackhttp.Mount(mux, gw)

完整契约(事件、状态存储、HTTP 形状、不变量)见 docs/module_design.md。

Index

Constants

View Source
const (
	// PathLive 直接路径:发出确认的运行仍存活,决定唤醒阻塞的运行。
	PathLive = "live"
	// PathRecovery 恢复路径:运行已不存在,网关代执行/合成结果并续跑。
	PathRecovery = "recovery"
)

决定实际生效的路径。

View Source
const (
	// EventName 是携带确认 Payload 的 AG-UI 自定义事件名。
	EventName = "tool_confirmation"

	// StateKey 是待确认表在 Session.State 中的键,
	// 让"等待确认"状态可以在服务重启后存活。
	StateKey = "__pending_tool_confirmation__"

	// StatusPending 表示确认正等待用户决定。
	StatusPending = "pending"
	// StatusApproved 表示用户已同意该确认。
	StatusApproved = "approved"
	// StatusDenied 表示用户已拒绝该确认。
	StatusDenied = "denied"
)

以下常量是 wire 与存储的兼容性契约:升级只增不改,否则遗留待确认 指针失联、前端事件配对失效。

Variables

View Source
var (
	// ErrNotFound 表示确认或会话不存在;含已决定(或已取消)后的
	// 重复决定与并发决定中的后到者。
	ErrNotFound = errors.New("ack: confirmation not found")

	// ErrNotBound 表示恢复路径需要续跑 runner 但未 Bind。
	ErrNotBound = errors.New("ack: resume runner not bound")

	// ErrRunActive 表示续跑时会话上仍有运行进行;对框架运行冲突
	// 错误包装,errors.Is 可判。
	ErrRunActive = errors.New("ack: run active on session")
)

Functions

This section is empty.

Types

type Basis

type Basis string

Basis 是授权终态的生效依据词表:一次工具调用的许可流程结束时, 标明放行或拒绝由哪条依据产生。该类型同时用作评估管线的内部依据 (evaluate 的返回值,供日志输出),内部值 basisForced / basisBaseline 保持未导出、不进入终态事件。

const (
	// BasisTier 层级依据:工具的许可层级直接放行或禁用。
	BasisTier Basis = "tier"
	// BasisVerdict 判定依据:首个 deny 判定的策略源拒绝。
	BasisVerdict Basis = "verdict"
	// BasisEndorsement 背书依据:首个 allow 判定的策略源将 confirm
	// 层级降级为放行。
	BasisEndorsement Basis = "endorsement"
	// BasisGrant 许可依据:人工预授予的许可命中放行。
	BasisGrant Basis = "grant"
	// BasisDuplicate 同会话重复确认 ID 的防御性放行。
	BasisDuplicate Basis = "duplicate"
	// BasisCanceled 运行取消或超时的结算拒绝。
	BasisCanceled Basis = "canceled"
	// BasisPersistFailed 确认请求持久化失败的结算拒绝。
	BasisPersistFailed Basis = "persist-failed"
)

type DecideResult

type DecideResult struct {
	Path string // PathLive | PathRecovery
}

DecideResult 是 Decide 的结果。

type Decision

type Decision struct {
	SessionID      string // 会话 ID
	ConfirmationID string // 确认 ID
	Approved       bool   // true=同意,false=拒绝
	DecidedBy      string // 决定人;留空取网关身份
	Remember       Scope  // 空=不生成许可;同意时按作用域写入许可源
}

Decision 是一次人工决定的完整参数;以结构体承载演进空间, 后续以增字段方式扩展。

type DenyTexts added in v0.2.1

type DenyTexts struct {
	// HumanDenied 人工拒绝(Decide 结算)的文案;live 与 recovery 路径一致。
	HumanDenied func(call ToolCall) string
	// TierDisabled 层级 deny 或策略 deny 判定拒绝的文案。
	TierDisabled func(toolName string) string
	// Canceled 运行取消或超时结算拒绝的文案。
	Canceled string
	// PersistFailed 待确认指针持久化失败结算拒绝的文案。
	PersistFailed string
	// Unavailable 恢复路径代执行时工具不可用/失败的文案;detail 为具体原因。
	Unavailable func(toolName, detail string) string
}

DenyTexts 定制各类拒绝时返回给模型的文案。零值字段使用库默认文案 (英文,含"不得重试"指令),自定义文案须自行保留该约束。

type Gateway

type Gateway struct {
	// contains filtered or unexported fields
}

Gateway 是库的核心对象:评估工具调用处置(放行/确认/拒绝),并承载 人工确认的完整生命周期——登记等待者、持久化待确认指针、结算决定、 恢复孤儿确认。Gateway 并发安全,可被多个 goroutine 同时调用。

网关不拥有会话存储、许可源与续跑 runner 的生命周期,三者由业务 构造与关闭。

func New

func New(svc session.Service, opts ...Option) *Gateway

New 创建许可网关。svc 为会话存储,由业务选择后端(框架自带 inmemory / postgres 等)并负责其生命周期。

func (*Gateway) Bind

func (g *Gateway) Bind(r ResumeRunner) error

Bind 注入恢复路径续跑使用的 AG-UI runner;传入 nil 返回错误。 恢复路径触发时未 Bind 的,Decide 返回 ErrNotBound。

func (*Gateway) Decide

func (g *Gateway) Decide(ctx context.Context, d Decision) (DecideResult, error)

Decide 结算一次人工决定,返回实际生效的路径(PathLive / PathRecovery)。 ctx 约束持久化与续跑的启动,不等待续跑完成。 同一确认的并发决定先到者生效,后到者返回 ErrNotFound。

func (*Gateway) Pending

func (g *Gateway) Pending(ctx context.Context, sessionID string) ([]PendingItem, error)

Pending 列出会话中等待决定的确认项:合并内存中的存活等待者与 Session.State 持久化的记录(含重启后的孤儿)。

func (*Gateway) PolicyFor

func (g *Gateway) PolicyFor(sessionID string) tool.PermissionPolicyFunc

PolicyFor 返回单个会话的权限策略:该会话的每次工具调用先经许可评估, 评估为确认的调用进入人工确认流程。供直接使用 core runner 的业务挂载; 经 AG-UI runner 接入的业务用 RunOptionResolver。

func (*Gateway) RunOptionResolver

func (g *Gateway) RunOptionResolver() func(ctx context.Context, in *adapter.RunAgentInput) ([]agent.RunOption, error)

RunOptionResolver 把许可策略适配为 aguirunner.WithRunOptionResolver 需要的形式,使每次 AG-UI 运行(含恢复路径的续跑)都挂上该策略。

type Grant

type Grant struct {
	UserID    string // 归属用户
	SessionID string // 非空即会话级;空即全局级
	ToolName  string
	CreatedBy string // 决定来源(人工决定的 DecidedBy)
	CreatedAt int64  // 毫秒时间戳
}

Grant 是用户对某工具的预先授权,可满足 confirm 层级的确认要求; 强制确认(策略源 confirm 判定触发)不可被许可满足。

type GrantQuery

type GrantQuery struct {
	UserID    string
	SessionID string
	ToolName  string
}

GrantQuery 是许可查询条件。

type GrantSource

type GrantSource interface {
	Lookup(ctx context.Context, q GrantQuery) (Grant, bool)
	Save(ctx context.Context, g Grant) error
}

GrantSource 是许可存储的接入点,由业务实现(如用户设置系统)。 生命周期归业务管理(列出/吊销/过期);库仅在许可评估时读取、在 Remember 决定时写入。Lookup 须按"会话级先于全局级"的次序命中。

func MemoryGrants

func MemoryGrants() GrantSource

MemoryGrants 返回线程安全的内存许可源,供测试与示例使用; 进程内生效,重启即失。

type NamedPolicy

type NamedPolicy struct {
	Name   string
	Source PolicySource
}

NamedPolicy 是带名字的策略源:名字进入授权终态事件的 Source 字段, 标明生效依据来自哪个外部裁决系统;空名字视同匿名(Source 为空)。

type Option

type Option func(*options)

Option 配置 Gateway。

func WithApp

func WithApp(name string) Option

WithApp 设置应用名(默认 "ack")。它是会话存储键的组成部分, 必须与 AG-UI runner 的 AppName 一致,否则会话定位失败。

func WithDefaultTier

func WithDefaultTier(t Tier) Option

WithDefaultTier 设置未分级工具的层级(默认 TierAllow)。

func WithDenyTexts added in v0.2.1

func WithDenyTexts(t DenyTexts) Option

WithDenyTexts 定制各类拒绝时返回给模型的文案(见 DenyTexts); 零值字段保持默认文案(英文、含"不得重试"指令)。

func WithGrants

func WithGrants(gs GrantSource) Option

WithGrants 设置许可源;缺省时许可评估视为永不命中,Remember 被忽略。

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger 设置结构化日志器(默认 slog.Default())。

func WithNamedPolicies

func WithNamedPolicies(ps ...NamedPolicy) Option

WithNamedPolicies 设置具名策略源,名字进入授权终态事件的 Source 字段,标明生效依据来自哪个外部裁决系统;与 WithPolicies 合成一条 咨询链(具名源在前、匿名源在后)。空名字视同匿名。

func WithPermissionObserver

func WithPermissionObserver(ob PermissionObserver) Option

WithPermissionObserver 设置授权终态观察者,订阅全部自动授权终态; 人工决定不通知,归宿主 Decide 路由审计。缺省 nil 零行为, 调用契约见 PermissionObserver。

func WithPolicies

func WithPolicies(src ...PolicySource) Option

WithPolicies 设置匿名策略源,每次工具调用逐个同步咨询;与 WithNamedPolicies 合成一条咨询链(具名源在前、匿名源在后),匿名 登记不产生授权终态事件的 Source。

func WithStaticTools

func WithStaticTools(tools ...tool.Tool) Option

WithStaticTools 是 WithTools 的静态糖:直接给出工具集合, 裁决时刻按声明名解析。

func WithTierFunc

func WithTierFunc(fn func(ctx context.Context, call ToolCall) Tier) Option

WithTierFunc 设置动态分级,可依据入参分类;优先于静态表, 静态表优先于默认层级。

func WithTiers

func WithTiers(m map[string]Tier) Option

WithTiers 按工具名静态分级。

func WithTools

func WithTools(res ToolResolver) Option

WithTools 设置工具解析器:恢复路径代执行时调用,返回"工具名 → 可调用工具"。缺省时同意的孤儿确认降级为"工具不可用"拒绝。

func WithUser

func WithUser(id string) Option

WithUser 设置固定用户 ID(默认 "user"),是会话存储键与许可查询的 组成部分。多用户场景改用 WithUserFunc。

func WithUserFunc

func WithUserFunc(fn func(ctx context.Context, threadID string) string) Option

WithUserFunc 设置动态身份:从请求上下文取鉴权结果 (鉴权中间件将用户写入 ctx)。优先于 WithUser。

type Payload

type Payload struct {
	ConfirmationID string          `json:"confirmationId"`      // 确认 ID(即工具调用 ID)
	ToolCallID     string          `json:"toolCallId"`          // 关联的工具调用 ID
	ToolName       string          `json:"toolName"`            // 工具名
	Arguments      json.RawMessage `json:"arguments,omitempty"` // 工具入参(JSON)
	SessionID      string          `json:"sessionId"`           // 所属会话 ID
	Status         string          `json:"status"`              // pending | approved | denied
	DecidedBy      string          `json:"decidedBy,omitempty"` // 做出决定的用户
	CreatedAt      int64           `json:"createdAt"`           // 创建时间(毫秒时间戳)
}

Payload 是一次工具确认的完整描述:随 AG-UI CustomEvent 下发前端, 同时以待确认指针的形式持久化在 Session.State 中。

func (Payload) CustomEvent

func (p Payload) CustomEvent() *aguievents.CustomEvent

CustomEvent 把 Payload 包装为 AG-UI 自定义事件。

type PendingItem

type PendingItem struct {
	Payload
	Live bool `json:"live"`
}

PendingItem 是待确认列表项;Live 标记阻塞中的运行是否仍存活 (false 表示已成孤儿,例如服务重启之后)。

type PermissionEvent

type PermissionEvent struct {
	// Call 是发生授权的调用视图。
	Call ToolCall
	// Allowed 是终态是否放行。
	Allowed bool
	// Basis 是生效依据,取值见 Basis 词表。
	Basis Basis
	// Source 是生效的具名策略源名:Basis 为 verdict 或 endorsement
	// 时是首个生效源的名字,其余依据与匿名策略源为空。已知信息损失:
	// 策略源咨询出错按 confirm 判定处理(fail-safe),该调用若最终以
	// canceled 结算,事件不携带出错源的名字,出错细节以网关日志为准。
	Source string
}

PermissionEvent 是一次工具调用的授权终态:该调用的许可流程已结束, 结论不再变更。人工决定(Decide 结算,live 与 recovery 路径均是) 不产生事件——每次工具调用恰好一条授权终态:要么 PermissionEvent、 要么 Decide 结算,二者互斥且共同覆盖全部调用;人工决定由宿主在 Decide 自身的路由与日志中审计。

type PermissionObserver

type PermissionObserver func(ctx context.Context, event PermissionEvent)

PermissionObserver 订阅网关产出的全部自动授权终态,供宿主外报审计。 网关在授权流程结束的同步路径上回调:回调应快速返回,重负载的外报 由宿主自行异步化。回调 ctx 经 context.WithoutCancel 处理、保证未取消 (取消/超时终态亦然),回调内可安全执行 IO,需要超时自行派生。 回调无返回值:观察不改变授权结论,回调内部错误由宿主自行处理。

type PolicySource

type PolicySource interface {
	Consult(ctx context.Context, call ToolCall) (Verdict, error)
}

PolicySource 是外部裁决系统(如风控)在库中的接入点,每次工具调用 被同步咨询。咨询出错时网关按 confirm 判定处理并记录日志(fail-safe: 裁决权威不可达时不静默放行);业务需要 fail-open 时,可在自身实现内 吞掉错误并返回 abstain。咨询位于工具调用的同步关键路径,源应低延迟 或由业务侧缓存。

type ResumeRunner

type ResumeRunner interface {
	Run(ctx context.Context, runAgentInput *adapter.RunAgentInput) (<-chan aguievents.Event, error)
}

ResumeRunner 与 AG-UI runner 的运行接口一致,恢复路径用它拉起续跑 运行。aguirunner.New 的返回值直接满足该接口。

type Scope

type Scope string

Scope 是许可的作用域。

const (
	// ScopeSession 会话级:绑定单个会话。
	ScopeSession Scope = "session"
	// ScopeGlobal 全局级:对该用户的全部会话生效。
	ScopeGlobal Scope = "global"
)

type Tier

type Tier string

Tier 是工具的基线许可层级。

const (
	// TierAllow 默认放行。
	TierAllow Tier = "allow"
	// TierConfirm 需人工确认;可被背书或已授予的许可满足。
	TierConfirm Tier = "confirm"
	// TierDeny 禁用;不可经判定、许可或人工决定放行。
	TierDeny Tier = "deny"
)

type ToolCall

type ToolCall struct {
	SessionID  string
	ToolCallID string
	ToolName   string
	Arguments  json.RawMessage
}

ToolCall 是评估与分级看到的调用视图。

type ToolResolver

type ToolResolver func(ctx context.Context) map[string]tool.CallableTool

ToolResolver 在裁决时刻返回"工具名 → 可调用工具"注册表,供恢复路径 代执行使用。做成函数形式使 MCP 等动态工具集可以在决定时刻实时解析。

type Verdict

type Verdict string

Verdict 是策略源对一次工具调用返回的意见。

const (
	// VerdictAbstain 无立场,不参与裁决。
	VerdictAbstain Verdict = "abstain"
	// VerdictAllow 背书:可将 confirm 层级降级为放行。
	VerdictAllow Verdict = "allow"
	// VerdictConfirm 强制确认:必须由人工决定,不可被许可满足。
	VerdictConfirm Verdict = "confirm"
	// VerdictDeny 拒绝:绝对,不可降级。
	VerdictDeny Verdict = "deny"
)

Directories

Path Synopsis
Package ackhttp 把确认决定/查询路由挂载到任意 http.ServeMux, 仅依赖标准库与 ack。
Package ackhttp 把确认决定/查询路由挂载到任意 http.ServeMux, 仅依赖标准库与 ack。
Package acktest 提供无真实 LLM 时的确定性测试夹具:脚本化模型 (model.Model 实现)与假续跑 runner,供库自身的测试与业务侧的 确认流集成测试使用。
Package acktest 提供无真实 LLM 时的确定性测试夹具:脚本化模型 (model.Model 实现)与假续跑 runner,供库自身的测试与业务侧的 确认流集成测试使用。
examples
notes command
notes 示例后端入口:基于 trpc-agent-go 的 AG-UI SSE 服务与 ack 许可网关。
notes 示例后端入口:基于 trpc-agent-go 的 AG-UI SSE 服务与 ack 许可网关。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL