Agent设计:纯推理句柄与状态分离

发布时间:2026/8/11 2:18:03

Agent设计:纯推理句柄与状态分离
前言agent 组件是 nexus agent 核心层的枢纽——所有能力在这里装配所有执行形态从这里发起。这篇讲它的形状为什么是这样。Agent——nexus 里的纯推理句柄。一句话定位只持能力不持状态。能力是 llm、tools、skills、interceptors、system prompt、maxSteps、mode状态会话历史、压缩、memory一个都不在它身上全在 AgentSession。这条边界是整个 nexus 最重要的一条。Agent 可以被任意多个 AgentSession 复用—— 100 万用户的客服系统按角色建几个 Agent账单、技术、退款每个用户一个 Session共享同一套 Agent 配置和模型 client。如果状态长在 Agent 上每个用户都得 new 一个模型 client 无法池化配置无法共享。核心设计本质上agent是一个粘合层负责把各种能力结合起来转发给LLM。agent的核心模型// Agent 是纯推理句柄只持能力llm/tools/skills/interceptors/system/maxSteps// 不持会话状态。一个 Agent 可跨多个 AgentSession 复用。// Run/Resume 走 reAct loop无 tools 时第一轮即退出等价 direct 单次调用// 有 tools 时多轮 tool 调用直至 stop。plan-execute 范式用 PlanExecAgent。type Agent struct {name stringdesc stringSystem stringllm llm.LLMClientskills *Skillstools []tools.TooltoolMap map[string]tools.TooltoolInterceptor []tools.ToolInterceptorllmInterceptor []llm.LLMInterceptorhooks *hooks.HooksmaxSteps intmaxPlanSteps intmode AgentMode}从核心模型能看到agent基本只有几个核心概念的组装外加tool和llm拦截器组成。初始化支持两种方式// NewAgent 轻量入口func NewAgent(name, system string, l llm.LLMClient, opts ...Option) *Agent {cfg : AgentConfig{Name: name, System: system, LLM: l}for _, o : range opts {o(cfg)}return NewAgentWithConfig(cfg)}// NewAgentWithConfig 重度/配置驱动入口直接传 AgentConfig。// 适合参数多、或从序列化配置加载的场景。func NewAgentWithConfig(cfg *AgentConfig) *Agent {cfg.apply()a : Agent{name: cfg.Name,desc: cfg.Desc,System: cfg.System,llm: cfg.LLM,tools: cfg.Tools,toolMap: make(map[string]tools.Tool, len(cfg.Tools)),skills: cfg.Skills,hooks: cfg.Hooks,toolInterceptor: cfg.ToolInterceptors,llmInterceptor: cfg.LLMInterceptors,maxSteps: cfg.MaxSteps,maxPlanSteps: cfg.PlanMaxSteps,mode: cfg.Mode,}if a.skills ! nil {a.System a.skills.Prompt(a.System)a.tools append(a.tools, a.skills.skillTools()...)}for _, t : range cfg.Tools {a.toolMap[t.Schema().Name] t}if a.hooks ! nil {a.toolInterceptor append(a.toolInterceptor, a.hooks.HookInterceptor)}return a}NewAgent比较轻量通过option的方式而NewAgentWithConfig则是直接通过配置构造这里值得额外说一句的是可以看到skill是如何构造并且注入到agent和llm的通过agent的system prompt加上自己的skill 重写了原来的system prompt也许不重写只是在run的时候构造更合理然后把skill 转化成tool给llm调用重写逻辑func SkillSystemPrompt(prompt string, ss ...*Skill) string {if len(ss) 0 {return prompt}var b strings.Builderb.WriteString(Available skills — if one seems relevant to the task, call read_skill(name) to load its full instructions before proceeding:\n)for _, s : range ss {b.WriteString(s.Summary()) // - name: desc\n}return b.String()}这是默认逻辑业务也可以自己替换type SkillOptions struct {systemPrompt skills.SystemPromptFuncloader skills.Loader}func (o *SkillOptions) apply() {if o.systemPrompt nil {o.systemPrompt skills.SkillSystemPrompt}if o.loader nil {o.loader skills.SkillLoader(skills.DirLoader)}}type SkillOption func(opts *SkillOptions)func WithSystemPrompt(systemPrompt skills.SystemPromptFunc) SkillOption {return func(opts *SkillOptions) { opts.systemPrompt systemPrompt }}func WithLoader(loader skills.Loader) SkillOption {return func(opts *SkillOptions) { opts.loader loader }}// Skills 支持skill注入agent。构造时加载并过滤掉依赖未满足的 skill// Prompt 把 skill 摘要含 read_skill 指令头拼进 system prompt// skillTools 暴露 read_skill / read_skill_resource 两个工具供 LLM 按需加载 skill 全文与资源。type Skills struct {systemPrompt skills.SystemPromptFuncloader skills.Loaderskills []*skills.Skill}还有就是通过拦截器把hook注入到每一次tool的调用进行拦截这样我们就可以很轻松实现比如pause审批human in the loop的能力。func (h *Hooks) HookInterceptor(next tools.ToolHandler) tools.ToolHandler {return func(ctx context.Context, params map[string]interface{}, s tools.Schema) (string, error) {for _, hook : range h.rules {if !hook.Matcher.Match(s, params) {continue}switch hook.Action {case ActionDeny:return hook.Reason, nilcase ActionAsk:// 命中规则 执行审批动作decision, err : h.fn(ctx, hook, params, s)if err ! nil {return , err}if decision.Action ActionDeny {return decision.Feedback, nil}default:}}return next(ctx, params, s)}}agent 需要实现核心接口// Agents agent 对外接口type Agents interface {Name() stringDescription() string// Run 单次任务执行Run(ctx context.Context, req *AgentRequest) (resp *llm.LLMResponse, err error)// Resume 对轮对话长任务有状态执行Resume(ctx context.Context, sess *AgentSession, req *AgentRequest) (*llm.LLMResponse, error)}// AgentsStream 流式执行能力按需实现。*AgentReAct/PlanExec实现// TransferAgent/Workflow 后续按需补。type AgentsStream interface {AgentsRunStream(ctx context.Context, req *AgentRequest) (-chan AgentEvent, error)//ResumeStream(ctx context.Context, sess *AgentSession, req *AgentRequest) (-chan AgentEvent, error)}这里拆分了两个接口一个是chat模式接口一个支持stream模式接口Run 是无状态单发每次现拼 system user 两条消息跑完即弃。无 tools 时第一轮就退出等价一次普通 Chat有 tools 时多轮 tool 调用到 stop。这是问一句答一句的场景。RunStream 是流式版run() 在后台 goroutine 跑通过 ch chan- AgentEvent 推事件AgentText/Reason/ToolCall/ToolResult/Final/Paused。ch 是否为 nil 是区分流式/非流式的开关——同一个 run()两种取数。注意 defer close(event) 和 ErrPaused 透传暂停不是错误不推 Err 事件。Resume 是有状态多轮核心是Agent 纯推理不管理 session 生命周期。Loadrehydrate由调用方在 Resume 前调Agent 不操心Agent 只负责把 sess.Messages() 当历史拼进 messages、跑完把新消息 sess.Append 持久化。具体实现func (a *Agent) Name() string {return a.name}func (a *Agent) Description() string {return a.desc}func (a *Agent) RunStream(ctx context.Context, req *AgentRequest) (-chan AgentEvent, error) {event : make(chan AgentEvent, 16)go func() {defer close(event)// ReAct 模式事件已在 loop 里推AgentText/Reason/ToolCall/ToolResult/Final/Paused// 这里只兜底非 paused 的错误。resp 无需再推AgentFinal 已在 ReAct 里推。if _, err : a.run(ctx, req, event); err ! nil !errors.Is(err, ErrPaused) {sendEvent(ctx, event, AgentEvent{Err: err})}}()return event, nil}// Run 无状态单发走 reAct loop。无 tools 时第一轮即退出等价 direct 单次调用。func (a *Agent) Run(ctx context.Context, req *AgentRequest) (resp *llm.LLMResponse, err error) {return a.run(ctx, req, nil)}这里可以看到不管是stream还是chat我们核心是统一的所以run可以复用起来。func (a *Agent) run(ctx context.Context, req *AgentRequest, ch chan- AgentEvent) (resp *llm.LLMResponse, err error) {messages : []llm.Message{{Role: system, Content: a.System},{Role: user, Content: req.Input},}if req.Mode 0 {req.Mode a.mode}switch req.Mode {case ReAct:resp, _, err a.ReAct(ch).Run(ctx, req, messages)case PlanExec:resp, _, err a.Plan(ch).Run(ctx, req, messages)}return resp, err}ReAct() 和 Plan() 是工厂方法返回 ReActAgent / PlanExecAgent——真正的循环执行器。Agent 本身不实现循环它只装配能力、分发到执行器。这样新增范式比如 Reflexion、Tree of Thought只需加一个执行器 一个 Mode 常量Agent 主体不动。循环是策略装配是机制又一条边界。Resume 有状态调用相比Run有状态需要记录每一次过程所以引入agent session进行独立存储压缩等处理。sess每次都会把ReAct 产生的new messages存储到内部方便统一处理在框架上很干净。func (a *Agent) Resume(ctx context.Context, sess *AgentSession, req *AgentRequest) (*llm.LLMResponse, error) {if sess nil {return nil, ErrNoSession}history : sess.Messages()messages : make([]llm.Message, 0, len(history)2)messages append(messages, llm.Message{Role: system, Content: a.System})messages append(messages, history...)messages append(messages, llm.Message{Role: user, Content: req.Input})// 持久化 usersess.Append(ctx, llm.Message{Role: user, Content: req.Input})resp, newMsgs, err : a.ReAct(nil).Run(ctx, req, messages)if err ! nil {if errors.Is(err, ErrPaused) {// 暂停落盘已产生消息透传 ErrPaused 供调用方识别续跑时 session 历史完整sess.Append(ctx, newMsgs...)return nil, err}return nil, err}// 持久化 新产生的消息sess.Append(ctx, newMsgs...)return resp, nil}最终都会转移到一个emit封装这个helper函数帮助agent屏蔽底层llm的chat和stream接口差异// emit 单轮 LLM 调用chnil 走非流式 Chat过 llmInterceptorch!nil 走 Stream转发 delta。// 两种模式都返回完整 *LLMResponseStreamFinal 或 Chat交给 ReAct 按 FinishReason 决定下一步。// 注意stream 路径暂未走 llmInterceptor待 StreamInterceptor 落地后补。func (a *Agent) emit(ctx context.Context, msgs []llm.Message, schemas []tools.Schema, ch chan- AgentEvent) (*llm.LLMResponse, error) {if ch nil {return llm.Chain(a.llmInterceptor...)(a.llm.Chat)(ctx, msgs, schemas)}stream, err : a.llm.Stream(ctx, msgs, schemas)if err ! nil {return nil, err}for {select {case -ctx.Done():return nil, ctx.Err()case chunk, ok : -stream:if !ok {return nil, fmt.Errorf(stream closed without final)}if chunk.Err ! nil {return nil, chunk.Err}switch chunk.Type {case llm.StreamText, llm.StreamReason:// 转发 delta 给上层展示StreamTool 累积态不转发ReAct 执行前推 AgentToolCall避免重复sendEvent(ctx, ch, AgentEvent{Type: AgentEventTypeFromLLM(chunk.Type),Response: chunk.Response,})case llm.StreamFinal:// 单轮结束不推事件——交 ReAct 按 FinishReason 决定 AgentToolCall/AgentFinalreturn chunk.Response, nil}}}}最后总结设计可以压缩成一句Agent 是纯推理句柄——构造时接线skills/hooks 自动注入运行时分发ReAct/Plan 工厂执行形态三选一Run/RunStream/Resume流式非流式同构emit。所有状态都被推到 AgentSession所有策略都被推到执行器和 hooksagent.go 自己只保留装配 分发这一层最薄的机制。这层薄机制是 nexus 整个 agent 体系的支点。

相关新闻

【OpenChamber技术解析】开源本地优先的跨端Agent开发环境

【OpenChamber技术解析】开源本地优先的跨端Agent开发环境

2026/8/11 2:08:03

文章目录OpenChamber技术解析:开源本地优先的跨端Agent开发环境一、引言二、从会话到开发闭环三、本地优先意味着什么四、采用前的现实检查五、OpenCode之上的工作区架构六、多模型并行不是简单“投票”七、从试用到团队落地八、代码审查如何适配Agent产出九、跨端与…

【图像处理算法入门】001. 课程导论与学习路线图:从零到一的144天

【图像处理算法入门】001. 课程导论与学习路线图:从零到一的144天

2026/8/11 2:08:03

系列文章目录 课程导论与学习路线图(本文)Python图像处理环境搭建:AnacondaOpenCVPillow数字图像的本质:从像素到矩阵…… 预计更新:2026.08.10 - 2026.12.31,每日1期,共144期 适合人群&#xf…

用过才敢说!盘点2026年倍受青睐的AI论文写作工具

用过才敢说!盘点2026年倍受青睐的AI论文写作工具

2026/8/11 2:08:03

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、降重润色、格式排版等核心场景,帮你高效搞定论文,真正实现写作自由。 一、全流程王者:一站式搞定论…

Intel AC9560无线网卡驱动故障排查与修复全攻略

Intel AC9560无线网卡驱动故障排查与修复全攻略

2026/8/11 3:28:06

1. 问题现象与初步诊断如果你打开Windows的设备管理器,在“网络适配器”那一栏里,看到那个本该代表Wi-Fi连接的“Intel(R) Wireless-AC 9560”前面挂着一个醒目的黄色感叹号,同时右下角的网络图标可能显示一个红叉或者地球图标,提…

解锁音乐自由:ncmdump让你轻松解密网易云NCM格式文件

解锁音乐自由:ncmdump让你轻松解密网易云NCM格式文件

2026/8/11 3:28:06

解锁音乐自由:ncmdump让你轻松解密网易云NCM格式文件 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经遇到过这样的困扰?在网易云音乐下载的歌曲只能在特定客户端播放,想要在其他设备或播…

TikTok新规解读与合规运营实战指南

TikTok新规解读与合规运营实战指南

2026/8/11 3:28:06

1. TikTok平台新规全景解读最近半年TikTok连续发布了十余项重要政策更新,主要集中在三个方向:内容审核标准升级、电商功能权限调整、创作者分成机制改革。作为深度参与TikTok生态的从业者,我梳理了其中最具杀伤力的五项核心变化:1…

PHP家政服务系统:智能派单与LBS监控实现

PHP家政服务系统:智能派单与LBS监控实现

2026/8/11 3:28:06

1. 项目背景与核心价值家政服务行业近年来呈现爆发式增长,据第三方统计数据显示,2023年中国家政服务市场规模已突破1.5万亿元。在这个背景下,传统家政公司面临三大痛点:手工派单效率低下、服务过程难以追踪、客户评价体系缺失。我…

前端 CSS 进阶知识库

前端 CSS 进阶知识库

2026/8/11 3:28:06

一、选择器权重等级(由低至高排序) 通配符选择器 * 元素标签选择器 类、属性、伪‑类选择器 ID 专属选择器 行内 style 内嵌样式 !important 拥有最高优先权,日常开发尽量少用 二、伪类、伪元素详解 伪类:操控元素各类状态 :hover…

魔兽争霸3兼容性修复终极指南:3步免费解锁完整功能

魔兽争霸3兼容性修复终极指南:3步免费解锁完整功能

2026/8/11 3:18:06

魔兽争霸3兼容性修复终极指南:3步免费解锁完整功能 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一款专为魔兽争霸3玩家…

比较好的亚太EMBA,问了6位校友师资差别真的挺大

比较好的亚太EMBA,问了6位校友师资差别真的挺大

2026/8/10 5:58:32

比较好的亚太EMBA核心差异先看什么?对于希望兼顾工作与系统管理能力提升的亚太区高管而言,筛选匹配度高的EMBA项目时,师资配置是决定学习体验与实际收获的核心要素之一。我们结合3-4个公开信息透明、办学历史较长的亚太区主流EMBA项目特点&am…

备考3个月对比6份资料 海外游学的亚洲EMBA面试注意点

备考3个月对比6份资料 海外游学的亚洲EMBA面试注意点

2026/8/10 7:54:12

备考海外游学的亚洲EMBA面试,核心要围绕项目国际化设计逻辑、个人跨文化管理经验匹配度两个维度准备,避免把游学模块等同于普通旅游参访的认知偏差。不少备考者花3个月对比6份资料,却容易忽略面试官对“国际视野落地能力”的考察——比如香港…

比较好的国内EMBA,问了二十位校友聊透人脉价值

比较好的国内EMBA,问了二十位校友聊透人脉价值

2026/8/10 7:19:21

比较好的国内EMBA核心差异体现在哪些方面?比较好的国内EMBA的核心长期价值,很大程度上依托于校友网络的连接质量与资源生态的活跃度,这也是不少高管在择校时优先考量的因素。我们结合3-4个市场关注度较高的项目公开信息,从课程、师…

Unity新手入门:从零搭建开发环境与核心概念解析

Unity新手入门:从零搭建开发环境与核心概念解析

2026/8/11 0:07:41

1. 项目概述:为什么Unity是游戏开发者的首选起点如果你对游戏开发感兴趣,或者想进入这个充满创造力的行业,那么“Unity”这个名字你肯定不陌生。它几乎是所有新手开发者、独立游戏团队,甚至是一些3A大厂在特定项目上的首选引擎。为…

Agency-Agents 智能体系统从零搭建实战指南

Agency-Agents 智能体系统从零搭建实战指南

2026/8/11 0:07:41

在开发复杂应用时,我们常常遇到单一模型难以兼顾全局规划与细节执行的困境。有时候,模型擅长创意生成却在逻辑推理上稍显吃力,或者精于代码编写却缺乏对业务上下文的深刻理解。为了解决这个问题,多智能体协作架构应运而生&#xf…

MiniMax 权益码 Token Plan 套餐 9 折优惠,Token Plan 共建邀请计划 至2026.8.31

MiniMax 权益码 Token Plan 套餐 9 折优惠,Token Plan 共建邀请计划 至2026.8.31

2026/8/11 0:07:41

🚀 MiniMax Token Plan MiniMax 推出全新 Token 计划,新增语音、音乐、视频和图片生成权益。 用户邀请好友可享双重福利 订阅一份套餐,解锁最新模型 —— 前沿 Coding 能力、1M 超长上下文、原生多模态,图文音视频共用套餐额度。 …

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/8 5:07:31

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/9 13:42:46

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/8 2:30:15

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…