Help us shape the future of Go! Take the 2026 Go Developer Survey

agentrun

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package agentrun ReAct agent 运行样板:封装 eino ADK 的 ChatModelAgent + Runner 构造、事件流 drain、最终文本提取与失败重试。

从 Argus 的 R3 执行模式提炼——任何"单 agent 带工具自主循环"的场景 都可以直接用,不必重写 ADK 样板:

out, err := agentrun.Run(ctx, agentrun.Config{
    Name:        "reviewer",
    Instruction: instruction,
    Model:       chatModel,           // llm.Generator.RawModel()
    Tools:       tools,               // eino 工具表(mcp/rag/skill...)
    MaxIterations: 12,
}, query)

ReAct 出口判定:assistant 消息且不带 tool_calls 即最终答复; MaxIterations 耗尽仍未出口 → 报错(调用方可决定降级)。

Plan-and-Execute 编排样板(架构模式 3):封装 eino adk prebuilt/planexecute 的 三件套(Planner / Executor / Replanner)——Planner 把目标拆解为分步计划, Executor 带工具执行单步,Replanner 评估进度决定"完成任务"或"修订计划", "执行→重规划"循环直至收敛。

与 ReAct(agentrun.Run)的分工:ReAct 适合"边想边做"的短链路任务;P&E 适合 目标明确、步骤可预规划的长链路任务(计划先行,每步执行结果可审计)。 需要定制 Planner/Replanner 提示词或输入整形时,直接使用 eino planexecute 原语。

副作用感知的重试守卫:变更类(非幂等)工具一旦执行,整体重跑 = 重复副作用 (脚本执行/服务操作类工具在首轮已真机生效)。重试入口据此跳过整体重试, 宁可如实失败交上层降级/人工,不做「重跑一次赌成功」。

变更类判定是保守的动词段启发式:按下划线分段**精确**匹配动词表——子串匹配会把 use_skill(含 kill)、get_running_config(含 run)误判成变更类,白白放弃合法重试; 偏保守原则保留在词表侧——拿不准的执行动词都收进来。

Index

Constants

View Source
const (
	EventReasoning      = "reasoning"
	EventText           = "text"
	EventToolCall       = "tool_call"
	EventToolResult     = "tool_result"
	EventReasoningDelta = "reasoning_delta"
	EventTextDelta      = "text_delta"
	// EventAttemptRetry 内部整程重跑标记(v0.14.7):WithRetry 的首跑失败→重试边界
	// 此前完全静默,时间线上只剩「两段近似重复环节」无从解释(bianque 批次二百九十五
	// 为协议级重试补过同款留痕,内部级是词表缺口)。Text=首跑失败原因(含救回标注)。
	EventAttemptRetry = "attempt_retry"
)

事件类型词表。 注意与 acpx 包事件词表(text/tool_call/tool_result)互为平行词汇: 两侧有意保持同名字面量以便消费方对译;改动任一侧词表时同步检查另一侧。

流式语义(默认开流):reasoning_delta/text_delta 在生成过程中逐分片到达 (真流式),同轮收束后仍发完整 reasoning/text 快照(权威口径,回放/对账以它为 准;增量只服务实况渲染)。消费方以「同轮快照到达即吸收增量缓冲」去重—— 中间**调用轮**无 text 快照(终稿窗口才有),其 text_delta 缓冲以该轮的 tool_call 事件为吸收锚(调用轮前导正文不构成终稿;reasoning 逐轮有快照), 拼接口径=各轮窗口正文按序拼接、终稿窗口与 EventText 快照严格一致(第八轮 审计钉住的词表契约)。

View Source
const DefaultMaxIterations = 12

默认迭代上限(ADK ReAct 循环轮数;防失控循环)。

Variables

View Source
var MutatingVerbs = map[string]bool{
	"exec": true, "execute": true, "run": true, "restart": true, "kill": true,
	"reboot": true, "shutdown": true, "halt": true, "start": true, "stop": true,
	"install": true, "remove": true, "delete": true, "del": true, "create": true,
	"write": true, "set": true, "update": true, "apply": true, "deploy": true,
	"enable": true, "disable": true, "flush": true, "truncate": true, "mount": true,
	"umount": true, "unmount": true, "killall": true, "chmod": true, "chown": true,
}

MutatingVerbs 变更类工具动词段表(可扩展:调用方按领域补充后 IsMutatingTool 即生效; 并发读取安全——构建期扩展、运行期只读)。

Functions

func IsMutatingTool

func IsMutatingTool(name string) bool

IsMutatingTool 工具名是否变更类(非幂等):小写后按下划线分段精确匹配动词表。

func Run

func Run(ctx context.Context, cfg Config, query string, opts ...RunOption) (string, error)

Run 执行一次 ReAct 查询,返回最终 assistant 文本;过程事件与失败重试经 RunOption 正交组合(WithOnEvent / WithRetry)。

Types

type Config

type Config struct {
	// Name agent 名(trace/日志标识)。
	Name string
	// Description 角色描述(进 agent 元信息)。
	Description string
	// Instruction 系统提示词(含方法论/工具策略等注入产物)。
	Instruction string
	// Model eino ChatModel(llm.Generator 的 RawModel())。
	Model model.BaseChatModel
	// Tools 工具表(建议经 toolprior.Table.Ordered(ctx) 产出)。
	// 与 ToolsFactory 二选一;两者都设置时 ToolsFactory 优先。
	Tools []tool.BaseTool
	// ToolsFactory 工具表工厂:每次 run 调用新建一份工具表。
	// WithRetry 场景建议设置——工具表含 toolprior.LimitCalls 等
	// 有状态包装时,复用同一实例会让限流计数跨重试累计(重试继承 0 余额,
	// 每次调用立即被拒)。工厂内每次重新包装即可让预算按尝试重置。
	ToolsFactory func() []tool.BaseTool
	// ToolAliases 工具**名别名**表(可选):键=工具的规范名(即工具表里
	// `tool.Info().Name` 的值,MCP 场景为 `server.工具名`),值=别名配置。
	// 模型用别名调用时会被解析回规范名,两种写法都命中。
	//
	// 为什么需要(实测 2026-10-30,扁鹊生产实弹 215):MCP 工具的暴露名带
	// server 命名空间(`ask-ops.run_readonly_commands`),而模型常按直觉发
	// **裸名**(`run_readonly_commands`)⇒ eino ToolsNode 直接报
	// `tool ... not found in toolsNode indexes`,该步骤**整步失败**
	// (事件错误向上抛、链路降级;变更类工具已执行时连整步重试都被跳过)。
	// 下游把裸名注册成别名 ⇒ 不必依赖模型自我纠正。
	//
	// 注意:别名若与**另一个工具的规范名或已注册别名**冲突,eino 侧
	// `applyAliasConfigs` 会让整个 ToolsNode 构造失败 ⇒ 装配期就要避免
	// (同名裸名来自两个 server 时只能择一,或改用 UnknownToolsHandler 兜底)。
	// nil/空=零行为(存量路径不变,eino 侧 aliasConfigs==nil 时跳过)。
	ToolAliases map[string]compose.ToolAliasConfig
	// UnknownToolsHandler **幻觉工具兜底**(可选):模型调用了工具表里
	// 不存在的工具时,由本处理器接管而不是让 eino 报错。返回的字符串会
	// 作为该次工具调用的结果回给模型,返回 error 则该步失败。
	//
	// 与 ToolAliases 的分工:别名解决**已知**的写法差异(首选——两种写法
	// 都命中,模型不必自我纠正);兜底解决**未知**的幻觉(模型编了个工具名),
	// 让它拿到一句可解释的观察而不是整步失败。
	//
	// 签名与 eino `compose.ToolsNodeConfig.UnknownToolsHandler` 一致:
	// name=模型请求的工具名,input=该次调用的参数原文(JSON 字符串)。
	// nil/空=零行为(存量路径不变:eino 侧未设时直接报错)。
	UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)
	// MaxIterations ReAct 循环轮数上限(默认 12)。
	MaxIterations int
	// RetryAfterMutation 变更类工具已执行后仍允许整体重试(默认 false=守卫生效:
	// 首轮调过变更类工具后失败不再重跑——重复副作用风险,如实上抛交调用方降级)。
	// 只读/幂等工具场景可置 true 恢复无条件重试。
	RetryAfterMutation bool
	// NonMutatingTools 工具名豁免集(键=工具名,小写归一比对):表内工具即使命中
	// 动词段启发式也不计变更类。消费方以 MCP ReadOnlyHint 注解/curated 白名单为据
	// 填充——动词启发式对 run_readonly_command 类「run*」只读执行器命名误报,实弹
	// (bianque 429 限流轮)首轮合法只读采集即挡死整轮重试,专家失败收场。
	// 与 RetryAfterMutation 正交:豁免集修正「哪些调用算变更」的判定输入,
	// RetryAfterMutation 决定「已判定变更后是否仍重试」。nil/空=零行为(纯启发式)。
	NonMutatingTools map[string]bool
	// DisableStreaming 关闭流式(默认 false=开):开流时思考/正文以 *_delta 增量事件
	// 外发(真流式观测面),每轮收流后仍按完整消息走既有派发与终稿判定——消费方
	// 语义不变,只多出增量通道。模型/网关不兼容流式时的逃生开关。
	DisableStreaming bool
	// OnUsage 流式调用的每轮用量回调(可选):开流后 eino callbacks OnEnd 拿到的是
	// StreamReader(无法在回调侧消费取 Usage),由 reactHandler 收流 concat 后从
	// ResponseMeta 提取并回调,每轮一次。非流式路径不触发(消费方沿用既有
	// callbacks 用量面板,两路不会双计)。ctx 为 run 的调用 ctx(归因取值用)。
	OnUsage func(ctx context.Context, u Usage)
	// ModelOptions 每次模型调用的 per-call eino Option(可选,批次 extra_body 透传缝):
	// 经 adk.WithChatModelOptions 注入 runner.Query——ADK 图执行时分发到 chat model
	// 节点的 Generate/Stream,随装饰链下钻到真实 ChatModel(openai.WithExtraFields
	// 等实现专属项即经此生效)。nil/空=零行为(存量路径不变)。流式与非流式两分支
	// 共用 Query 入口故一并覆盖;FailoverModel 会把同一批 opts 转发给主/备链——
	// 端点特定的扩展字段需调用侧自觉仅对认得它的档位设置(多数 OpenAI 兼容网关忽略
	// 未知字段,严格网关可能 400)。
	ModelOptions []model.Option
}

Config ReAct agent 运行配置。

func (Config) Validate

func (c Config) Validate() error

Validate 校验配置必需项。

type Event

type Event struct {
	Type   string // reasoning | reasoning_delta | text | text_delta | tool_call | tool_result
	Text   string // *_delta 时为增量分片(非累积快照)
	Tool   string // tool_call/tool_result 的工具名
	Args   string // tool_call 的 JSON 参数串
	CallID string // tool_call/tool_result 的原生调用 ID(声明 tc.ID / 结果 ToolCallID)——观测面精确配对依据
}

Event agent 运行过程事件(OnEvent 回调载荷,观测/进度展示用)。

type FinalMissingError

type FinalMissingError struct {
	EmptyContent  bool
	Iterations    int
	LastReasoning string // 最后一轮 assistant 消息的 reasoning_content(可能为空)
}

FinalMissingError 终稿缺失错误(可重试形态):迭代耗尽未出口(EmptyContent=false) 或最终消息正文为空(EmptyContent=true——推理型模型把终稿只写进思考过程的形态, 2026-10-13 bianque kylin-insight 实弹:qwen3.8-flash 12.7k 字分析全在 reasoning, content 空 → 整程重跑 32 次只读调用重放)。携带最后一段思考片段供重试侧救回: 分析已完成时重试查询附上存档,模型直接产出终稿,不再从头重放工具采集。

func (*FinalMissingError) Error

func (e *FinalMissingError) Error() string

type PlanExecuteConfig

type PlanExecuteConfig struct {
	// Planner 规划模型(须支持 tool calling——eino-ext openai ChatModel 均满足)。
	// 计划结构经默认 Plan ToolInfo 以 tool-calling 形态强制产出。
	Planner model.ToolCallingChatModel
	// Replanner 重规划/完成判定模型(须支持 tool calling;空 = 复用 Planner
	// 模型——eino planexecute.Config 的 Replanner 是必填项,agentrun 在此
	// 兜底装配。生产可配更便宜的模型跑判定)。
	Replanner model.ToolCallingChatModel
	// Executor 执行模型(带工具时同样须支持 tool calling)。
	Executor model.ToolCallingChatModel
	// Tools 执行器可用工具(nil = 纯推理执行)。
	Tools []tool.BaseTool
	// ToolAliases 工具**名别名**表(可选):语义同 Config.ToolAliases——
	// 键=规范名(`tool.Info().Name`,MCP 场景为 `server.工具名`),
	// 值=别名配置;模型用别名调用时解析回规范名。
	// MCP 暴露名带 server 命名空间、模型常发裸名 ⇒ 不注册别名则该步整步失败
	// (eino 报 `tool ... not found in toolsNode indexes`)。
	// nil/空=零行为(存量路径不变)。
	ToolAliases map[string]compose.ToolAliasConfig
	// UnknownToolsHandler 幻觉工具兜底(可选):语义同 Config.UnknownToolsHandler——
	// 模型调用不存在的工具时由它接管,返回的字符串作为该次调用的结果回给模型。
	// nil/空=零行为(存量路径不变:eino 侧未设时直接报错)。
	UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)
	// MaxSteps "执行-重规划"循环上限(默认 10)。
	MaxSteps int
	// PlannerInstruction 追加给规划器的约束/背景(可空;经输入整形注入,
	// 不覆盖 planexecute 默认的计划格式提示词)。
	PlannerInstruction string
}

PlanExecuteConfig Plan-and-Execute 运行配置。

type PlanExecuteResult

type PlanExecuteResult struct {
	// Text 最终答复(Replanner 判定任务完成时产出)——与 acpx.RunResult.Text、
	// reflection.RefineResult.Text 同词表。
	Text string
}

PlanExecuteResult 运行结果。

func PlanAndExecute

func PlanAndExecute(ctx context.Context, cfg PlanExecuteConfig, goal string) (*PlanExecuteResult, error)

PlanAndExecute 运行 Plan-and-Execute。

type RunOption

type RunOption func(*runOpts)

RunOption Run 入口的可选行为(v0.10.38 破坏式收敛:此前 Run/RunWithEvents/ RunWithRetry/RunWithEventsAndRetry 四个导出入口层层包裹——retryQuery 是 独立参数而 RetryAfterMutation 却在 Config,签名不对称;入口唯一后行为 正交组合,守卫逻辑只此一份)。

func WithOnEvent

func WithOnEvent(fn func(Event)) RunOption

WithOnEvent 过程事件回调(可多传,末个生效;nil 回调等价不传)。 契约:回调在事件 drain 循环内同步执行——不得阻塞(阻塞会拖停整轮 agent)、 不得 panic(会击穿调用方 goroutine,与 acpx OnEvent 同契约;事件已固化, 回调内消费失败不影响事件面完整性,自行 recover)。

func WithRetry

func WithRetry(retryQuery string) RunOption

WithRetry 失败回喂重试一次:首次失败(或产出空文本)时以 retryQuery 再跑, 两次均失败返回末次错误。副作用守卫:首轮已调用变更类工具(见 IsMutatingTool)后不整体重跑,除非 Config.RetryAfterMutation=true——重跑会 重复副作用(脚本执行/服务操作类工具在首轮已生效)。 ⚠ retryQuery 是**完整替换** query 而非追加(2026-09-19 bianque 实弹教训: 消费者若传纯提示语,重试轮将丢失全部任务材料——工单/合议上下文全空,模型 输出「未收到输入」类空心合规报告)。消费方应传 query+提示语的拼接串。 注意:两次尝试共用 cfg.Tools 实例——工具表含 toolprior.LimitCalls 等有状态 包装时,限流计数会跨尝试累计;需要按尝试重置预算请设置 ToolsFactory。

type SideEffectTracker

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

SideEffectTracker 观测一次 agent 运行中是否调用过变更类工具(事件回调包装器, 观测不截断:Wrap 后事件照常透传给原回调)。exempt 为工具名豁免集 (Config.NonMutatingTools 注入;零值=无豁免,纯动词启发式)。

func (*SideEffectTracker) Called

func (t *SideEffectTracker) Called() bool

Called 是否已调用过变更类工具。

func (*SideEffectTracker) MutatingTool

func (t *SideEffectTracker) MutatingTool() string

MutatingTool 首个被调用的变更类工具名(未调用返回 "")。

func (*SideEffectTracker) Observe

func (t *SideEffectTracker) Observe(ev Event)

Observe 处理一个运行事件(接 OnEvent 回调)。

func (*SideEffectTracker) Wrap

func (t *SideEffectTracker) Wrap(onEvent func(Event)) func(Event)

Wrap 包装事件回调:记录变更类工具调用并透传事件(onEvent 可为 nil)。

type Usage

type Usage struct {
	PromptTokens     int
	CachedTokens     int
	CompletionTokens int
	ReasoningTokens  int
	TotalTokens      int
	FinishReason     string
	// DurationMS 本轮 LLM 调用的墙钟耗时(毫秒)——流式路径自模型流交付
	// (拿到本轮 ResponseMeta 的流)到收流 concat 完成的时长,口径与
	// llm.UsageRecord.DurationMS 同义(time.Since(start).Milliseconds())。
	//
	// 0 = 未采集。消费方据此区分「零耗时」与「没采到耗时」——字段缺失时两者
	// 不可区分(消费侧只能记 0,看板上表现为「数据全 0」,比没有数据更坏)。
	//
	// 口径边界:起点是模型流交付时刻,**不含**「请求发出到首帧到达」的排队
	// 与首字延迟(TTFB);终点是末帧收齐并 concat 完成的时刻。
	DurationMS int64
}

Usage 单轮 LLM 调用用量(流式 OnUsage 回调载荷;字段与 eino TokenUsage 同义)。

Jump to

Keyboard shortcuts

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