从零推导多 Agent 协作网络 (Flow Agent)

拨开迷雾看本质:从零推导多 Agent 协作网络 (Flow Agent)

1. 为什么我们需要多个 Agent 协作?

在简单的场景中,一个拥有各种工具的单体 Agent(如我们在 react.md 中推导的那样)就足够了。

但当业务变得复杂时,单体 Agent 很容易出现一些系统性问题:

  1. Prompt 爆炸:如果你让一个 Agent 既懂写代码、又懂审查、还要会发邮件,它的 System Prompt 会长到让大模型"失焦"。
  2. 工具灾难:如果给它塞 50 个工具,模型根本选不过来,甚至会把查天气的工具和删数据库的工具搞混。

朴素的想法:

既然一个 Agent 搞不定,我们就把任务拆分给多个 专职 的 Agent。

假设我们手写一个极简的"多 Agent 协作"代码:

go 复制代码
// 朴素的多 Agent 协作
func RunMultiAgent(input string) {
    // 1. 经理分析需求
    managerPlan := managerAgent.Run(input)
    
    // 2. 如果需要写代码,就调 Coder
    if strings.Contains(managerPlan, "code") {
        codeResult := coderAgent.Run(managerPlan)
        // 3. 代码写完,调 Reviewer
        reviewResult := reviewerAgent.Run(codeResult)
        fmt.Println("最终结果:", reviewResult)
    }
}

看起来很清晰?但这只是在写 硬编码的脚本 ,一旦业务链路变长、需要 Agent 之间互相讨论(比如 Reviewer 觉得不行,打回给 Coder 重新写),这种用 if/else 手工调度的代码就会彻底失控。


2. 第一步演进:解决硬编码调度(Transfer 机制)

问题场景

我们希望 Agent 能够 自主决定 下一个任务交给谁,而不是在外部代码里用 if/else 写死。比如 Manager 发现问题很复杂,它自己决定"把任务转交(Transfer)给 Coder"。

批判性思考

要让 Agent 自主转交,我们需要赋予它一种"交接棒"的能力。

最自然的想法是:把"转移给另一个 Agent"做成一个 Tool 塞给当前 Agent

引入 Transfer 机制后的本质代码

go 复制代码
// 框架层:定义一种特殊的工作流引擎
type FlowEngine struct {
    agents map[string]Agent
}

func (e *FlowEngine) Run(startAgentName string, history []Message) {
    currName := startAgentName
    
    for currName != "" {
        agent := e.agents[currName]
        
        // 执行当前 Agent
        resp := agent.Run(history)
        history = append(history, resp)
        
        // 检查这个 Agent 是否调用了特殊的 Transfer 工具
        if resp.HasToolCall("TransferTo") {
            // 解析它想转给谁
            nextAgent := resp.ToolCalls[0].Args["target_agent"]
            currName = nextAgent // 接力棒传下去了!
        } else {
            // 如果没调 Transfer,说明任务结束了
            currName = ""
        }
    }
}

在这个模型下,CoderReviewer 可以互相 Transfer,形成一个死循环辩论,而你不需要在外部写任何一行 while 循环。

补充:Transfer 的数据契约

上面的伪码默认 Transfer 只是在"换一个 Agent 继续读同一段 history"。这能跑通,但也会自然引出一个工程问题:交接时到底传什么?

  1. 如果只传 history,结构化状态(例如计划、约束、工具调用的结果对象)会被迫编码进文本,形成隐式协议。
  2. 如果额外传 state,就需要一个明确的 Schema(强类型或至少可校验),否则状态会在多个 Agent 之间逐步漂移。

更稳妥的理解方式,是把 Transfer 的 "数据契约" 拆成两层:一层是 显式控制面 ,另一层是 隐式数据面

  1. 显式控制面:Transfer 的职责是表达 "把控制权交给谁" 。它可以额外携带少量观测信息(例如 reason / tag),用于复盘与可观测,但不应承载业务数据,否则交接会退化为 "在 Agent 之间传包",并在链路变长后迅速演化出不可维护的隐式协议。
  2. 隐式数据面:上下文与状态应放在一个共享的会话容器里,并由框架贯穿整个协作链路。在 Eino 的实现中,Transfer 的显式载荷非常薄(目标 Agent 名),下一个 Agent 之所以仍能接着跑,是因为它继续共享同一个 runCtx(RootInput + Session.Events + Session.Values)。因此,许多看起来像 "state" 的信息,最终更像是 "会话 KV" 或 "事件序列" 的投影,而不是 Transfer 参数本身。

若要把这套模式做得更工程化,改进方向通常也落在共享容器上,而不是往 Transfer 参数里塞更多字段:

  1. 为 Session.Values 引入最小 Schema:至少做到可校验(版本号 + 必需字段),并明确 key 命名空间,避免不同 Agent 互相踩踏或让状态在链路中漂移。
  2. 把大对象从 history 搬走:history 更适合承载决策与结论;工具大结果、结构化中间态更适合放在 Session.Values 中,只在 history 里引用一个摘要或 ID。
  3. 将交接做成可观测事件:即使 Transfer 只传目标名,也应记录交接原因与关键上下文摘要,方便在事后回答 "为什么会跳到这个 Agent" 。

3. 第二步演进:解决"记忆错乱"与"鸡同鸭讲"(History Rewrite)

问题场景

在上面的交接棒模式中,大家共享同一个 history []Message

想象一下:

  1. Coder 写了一段代码,并在 history 里记录了 assistant: 好的,代码写完了。
  2. 接力棒传给了 Reviewer
  3. Reviewer 看到历史记录里写着 assistant: 好的,代码写完了。
    灾难发生了! Reviewer 会以为这句话是 自己 刚才说的!它会陷入精神分裂,无法区分哪句话是 Coder 说的,哪句话是自己说的。

批判性思考

大模型本身是没有"自我意识"的,它只认 Role(user/assistant/tool)。在多 Agent 网络中,不同 Agent 轮流扮演 assistant 会导致极其严重的身份混淆。

我们必须在交接棒时,翻译(Rewrite)历史

引入 History Rewriter 后的本质解法

go 复制代码
// 每次交接给新 Agent 前,把之前的记录"翻译"成用户口吻
func RewriteHistory(history []Message, nextAgentName string) []Message {
    var newHistory []Message
    
    for _, msg := range history {
        if msg.Role == "assistant" && msg.Author != nextAgentName {
            // 关键:把别的 Agent 说的的话,伪装成 User 对当前 Agent 说的背景信息
            rewritten := fmt.Sprintf("For context: [%s] said: %s", msg.Author, msg.Content)
            newHistory = append(newHistory, Message{Role: "user", Content: rewritten})
        } else {
            newHistory = append(newHistory, msg)
        }
    }
    return newHistory
}

通过这种转换,当 Reviewer 接手时,它看到的是 user: For context: [Coder] said: 好的,代码写完了。这样它的"自我认知"就不会错乱。

补充:History Rewrite 的工程代价

History Rewrite 是一个非常有效的 "兜底工程技巧",但它也会带来一些确定的代价:

  1. 上下文膨胀:每条消息都增加前缀与转述信息,链路越长越容易触发摘要/裁剪,从而改变后续 Agent 的判断依据。
  2. 语用漂移 :把别的 Agent 的发言改写成 user 背景,会把对等协作语境悄悄变成"用户指令 + 背景材料",影响模型的服从与质疑倾向。
  3. 注入面扩大 :如果子 Agent 输出包含诱导性文本,改写为 user 后风险更高;更稳的长期方向通常是结构化消息(保留 Author、RunPath、State)而不是纯字符串拼接。

4. 第三步演进:解决网络层级与事件风暴(Flow Agent Wrapper)

问题场景

随着 Agent 越来越多,扁平的"交接棒"不够用了。我们需要 层级结构(Supervisor 模式)

比如:MainAgent 包含子 Agent [Coder, Reviewer]

这时候会出现两个典型痛点:

  1. 事件风暴:前端想要看到实时的打字机效果。但由于有多个嵌套的 Agent 轮流运行,到底该监听谁的输出?如果 Coder 和 Reviewer 在疯狂对骂,前端怎么把这些流式的事件(Event)有序地拼凑起来?
  2. 中断与恢复地狱 :如果 Coder 在中间调用工具挂起了。系统恢复时,你怎么知道是去唤醒 MainAgent 还是 Coder

批判性思考

我们不能把多 Agent 当成简单的"平级函数互相调用"。我们需要一个 统一的包装器(Wrapper) ,把所有 Agent 包装成一样的接口。这个包装器负责:

  1. 拦截和聚合事件:把各个子 Agent 零散的事件打上时间戳和标签,汇聚到一根总的管子里。
  2. 拦截和翻译交接棒(Transfer):截获底层的 Transfer 事件,根据层级结构找到目标 Agent。
  3. 处理横切关注点(Cross-cutting Concerns) :在真实业务中,我们不仅要调 Agent,还要在每个 Agent 执行前后做打点追踪(Tracing)、权限校验、统一的异常捕获 。如果把这些代码塞进每个 Agent 内部,代码会极度臃肿。包装器正好可以在调用 realAgent.Run 前后,插入这些"横切逻辑"。

引入 Flow Wrapper (装饰器模式) 的本质代码

我们需要一个 统一的包装器(Wrapper) ,把所有 Agent 包装成一样的接口。这个包装器负责:

  1. 统一输入输出:把零散的历史记录聚合成统一的输入,把乱飞的事件打上标签聚合成统一的输出流。
  2. 拦截和翻译交接棒(Transfer):截获底层的 Transfer 事件,根据层级结构递归调度下一个 Agent。
  3. 精准路由恢复(Resume):当收到恢复信号时,根据信号里的"目的地名字",决定是自己恢复,还是把信号透传给某个子 Agent。

在这个模型里,有一个经常被忽略但很关键的隐含前提:事件流的顺序需要足够稳定。否则前端渲染、日志对齐与恢复定位都会变得很困难。最简单的方式是保持 "单线程、深度优先" 的运行模型;一旦引入并发子 Agent,就需要额外的排序键(例如时间戳 + 运行路径 + 事件序号)来重建可重放顺序。

go 复制代码
// 框架层:代理包装器 (装饰器模式)
// 它包装了一个真实的 Agent,并为它处理历史翻译、事件转发、调度和恢复
type FlowAgent struct {
    realAgent Agent
    subAgents map[string]*FlowAgent
    name      string
}

// ---------------------------------------------------------
// 核心逻辑 1:如何组织输入和输出 (Run)
// ---------------------------------------------------------
func (f *FlowAgent) Run(globalHistory []Message, outChan chan Event) {
    // 【输入组织】:大模型容易精神分裂,所以在真正调底层 Agent 前,先把全局历史翻译成 "For context: [XXX] said: ..."
    rewrittenHistory := RewriteHistory(globalHistory, f.name)
    
    // 创建一个拦截器 channel,用来"偷听"真实 Agent 的动静
    innerChan := make(chan Event)
    go f.realAgent.Run(rewrittenHistory, innerChan)
    
    // 【输出组织】:循环处理底层冒出来的每一个事件
    for event := range innerChan {
        // 1. 打上当前 Agent 的水印,防止多层嵌套时前端分不清是谁说的
        event.AgentName = f.name 
        
        // 2. 拦截 Transfer 事件 (解决交接棒问题)
        if event.IsTransfer() {
            targetName := event.TransferTarget
            targetAgent := f.subAgents[targetName]
            
            // 递归调度!把接力棒传给下一个 Agent,并把事件无缝拼接到当前的总输出流中
            // 注意:因为 targetAgent 也是个 FlowAgent,所以它也会经历历史翻译、水印等过程
            targetAgent.Run(event.CurrentHistory, outChan)
            return // 当前 Agent 的任务被交接出去了,自己的 Run 结束
        }
        
        // 3. 普通事件,直接透传给外层的输出管子
        outChan <- event 
    }
}

// ---------------------------------------------------------
// 核心逻辑 2:如何解决中断与恢复地狱 (Resume)
// ---------------------------------------------------------
// resumeInfo 里包含了用户当初恢复时指定的"目标组件名字"
func (f *FlowAgent) Resume(resumeInfo ResumeInfo, outChan chan Event) {
    // 1. 如果中断发生在我这层(比如我自己调的某个 Tool 挂起了),那我自己恢复
    if resumeInfo.TargetAgentName == f.name {
        f.realAgent.Resume(resumeInfo, outChan)
        return
    }
    
    // 2. 如果中断不发生在我这层,说明中断发生在我的某个子孙节点里
    // 这时候我就像个路由器一样,查一下路由表(subAgents),把恢复信号原封不动地踢给对应的子节点
    targetAgent := f.subAgents[resumeInfo.TargetAgentName]
    if targetAgent != nil {
        targetAgent.Resume(resumeInfo, outChan)
        return
    }
    
    panic("找不到要恢复的 Agent!")
}

这个路由逻辑隐含了一个约束: TargetAgentName 必须稳定且唯一。层级网络里仅靠短名很容易冲突,更稳的做法通常是使用路径化的标识(例如 root.reviewer)或运行时分配的 ID,并在 checkpoint 里保存该标识以保证跨版本可恢复。

通过这个 FlowAgent 装饰器:

  • 输入/输出不再混乱 :外部的调用方只需要往最外层的 FlowAgent 传一次输入,监听一根 outChan。内部不管互相怎么 Transfer、怎么嵌套,最终所有的水滴都会被打上 AgentName 的标签,从这根唯一的管子里流出来。
  • 恢复不再迷路 :当外部调用 rootAgent.Resume 时,恢复信号会像网络数据包一样,沿着装饰器链一层层往下传,直到精准命中当初挂起的那个真实 Agent。

5. 补充思考:Flow 与 ReAct 的关系

在理解了 Flow 的包装器本质后,我们必须回答一个关键问题:被包装在里面的那个 realAgent 到底是什么?Flow 和 ReAct 是什么关系?

答案是:Flow 是编排层(网络),ReAct 是执行层(节点)。

在绝大多数情况下,Flow 包装的 realAgent 就是一个 ReAct Agent(也就是 Eino 里的 ChatModelAgent)

ReAct 如何支撑了 Flow?

如果在 flow.md 里我们推导的 Transfer(交接棒)机制显得过于"理想化",那是因为我们没有把它和 ReAct 结合起来看。

  1. "思考然后交接"的闭环

    Flow 只是规定了"一旦出现 Transfer 事件,我就帮你转发"。但谁来产生这个 Transfer 事件?

    是底层的 ReAct Agent!ReAct Agent 的核心能力是 Reasoning + Acting。当它(模型)经过 Reasoning 发现自己搞不定,或者发现任务应该归属下一环节时,它就会选择调用一个特殊的 Tool(比如 TransferToAgent)。这个 Tool 的执行结果(Acting),就是吐出一个 Transfer Action,从而触发了外层 Flow 的调度。
    没有 ReAct 的智能决策,Flow 就是一个无法触发的死网络。

  2. 状态挂起的降维打击(宏观路由 vs 微观现场)

    在 Flow 中,我们遇到了"中断与恢复地狱"。虽然 Flow 的 Resume 方法像路由器一样解决了"把信号踢给谁"的问题(Agent 层面的 Resume ),但最终那个子节点是怎么真正挂起和恢复的呢?

    这完全依赖于底层的 ReAct 框架!正如我们在 react.md 中推导的,ReAct 底层的图引擎(Graph)和状态机(State)才是真正保存 Checkpoint 和执行断点续传的功臣(工具层面的 Resume)。

    • ReAct 负责"微观现场":它记住"我正在调第 3 个工具,工具叫 AskUser,参数是什么"。
    • Flow 负责"宏观路由" :它记住"当前是 Coder 在干活,所以我要把唤醒信号发给 Coder,而不是 Reviewer"。
      Flow 站在了 ReAct 的肩膀上,把微观节点的挂起能力,扩展成了整个宏观网络的挂起能力。

6. 映射到现实:Eino 的 adk/flow.go 到底做了什么?

当我们审视真实的 adk/flow.go 时,你会发现它的核心逻辑就是我们上面推导的 第三步(Flow Wrapper)

Eino 的 flowAgent 是一个非常典型的 装饰器(Decorator) 模式。

  1. 解决"鸡同鸭讲"L171-L200 rewriteMessage 函数,正是把其他 Agent 的 assistant 发言,改写成了 For context: [xxx] said:user 发言,显著缓解了大模型的身份错乱问题。
  2. 解决"调度交接"L468-L488if destName != "" 逻辑。当底层的 Agent 吐出一个 Transfer 动作时,flowAgent 把它截获了,然后从自己的 subAgents 列表里找出目标 Agent,递归调用 subAIter := agentToRun.Run(...)
  3. 解决"事件风暴与归属"L429-L440exactRunPathMatch 逻辑。因为多层嵌套,事件会在 channel 里乱飞。flowAgent 通常需要通过 RunPath 匹配,来决定哪些事件属于"我自己这层的运行",应该记录到 Session 里,哪些事件只是子节点的噪音,应该忽略。

6.1 深入 Transfer 工具的"原生注入"机制

在前面的推导中,我们把"转移给另一个 Agent"简单地说成是"塞给当前 Agent 的一个 Tool"。在 Eino 的真实实现中,这个注入过程隐藏着极深的架构考量。

当你调用 adk.SetSubAgents(ctx, parentAgent, []Agent{childAgent}) 构建 Flow 网络时,ChatModelAgent 会在准备执行上下文时( <adk/chatmodel.go#L636-L657> prepareExecContext)原生地注入 Transfer 能力:

go 复制代码
// 1. 获取所有可以 Transfer 的目标(子节点 + 父节点)
transferToAgents := a.subAgents
if a.parentAgent != nil && !a.disallowTransferToParent {
    transferToAgents = append(transferToAgents, a.parentAgent)
}

// 2. 如果存在可交接的目标,原生地注入 Instruction 和 Tool
if len(transferToAgents) > 0 {
    // 动态生成 Transfer 的专属提示词,大模型看到的极简版本大致如下:
    // "可用的其他 Agent:
    //  - Coder:负责写代码
    //  - Reviewer:负责审查
    // 决策规则:如果你自己搞不定,请调用 'transfer_to_agent' 函数,把活交出去。"
    transferInstruction := genTransferToAgentInstruction(ctx, transferToAgents)
    instruction = concatInstructions(instruction, transferInstruction)

    // 强行把 transfer_to_agent 工具塞进列表
    toolsNodeConf.Tools = append(toolsNodeConf.Tools, &transferToAgent{})
    
    // 【核心黑科技】:标记这个工具为"立即返回"类型,用于打断 ReAct 的内部死循环
    returnDirectly[TransferToAgentToolName] = true
}

为什么是硬编码原生注入,而不是用 Middleware 包装?

这揭示了 Transfer 的本质:它不是一个普通的"业务工具(Tool)",而是改变控制流的"底层汇编指令(JMP)"

  • Transfer 必须修改底层 ReAct 引擎的控制流:一旦大模型调用了它,Agent 必须立刻结束当前生命周期(returnDirectly),把控制权交还给外层的 Flow 引擎去唤醒下一个节点。
  • Transfer 必须感知网络的父子拓扑:它需要动态知道自己当前挂载了哪些子节点、谁是父节点,而 Middleware 是一种无状态的请求拦截器,不适合维护图拓扑。

7. 批判性总结:Flow 究竟是"标准"还是"选项"?

在阅读完上述分析后,极易产生一个误解:既然 Flow 能解决这么多问题,是不是所有的多 Agent 协作底层都在用 flowAgent

答案是:绝对不是。

为了搞清 adk/flow.go 和各种 Prebuilt Agent(预置模式)的关系,我们必须做一次彻底的隔离辨析。

7.1 核心辨析:flowAgent 只是一种特定的网络协议,不是底层标准!

在 Eino 的世界里,真正的大一统底层标准是 adk.Agent 接口 。只要你实现了 RunResume,你就是一个合法的 Agent。

flowAgent(通过 SetSubAgentsAgentWithOptions 创建),仅仅是为了实现 Transfer(异步变轨交接) 这种特定的协作模式,而特化出来的一种"网络协议包装器"。

这就好比:Agent 接口是 TCP/IP 协议,而 flowAgent 是 HTTP 协议。你可以用 HTTP 聊天,但你完全可以绕过 HTTP,自己用 TCP 写一套专用的 RPC(比如 Deep Agent 和 PlanExecute)。

7.2 映射到 Prebuilt 模式:谁在用,谁没用?

  1. Supervisor 模式:重度依赖 Flow

    • 源码证明 :在 adk/prebuilt/supervisor/supervisor.go 中,核心逻辑正是调用了 adk.SetSubAgents,并用 DeterministicTransferTo 包装了子节点。
    • 原因 :Supervisor 的灵魂就是"星型网络的 Transfer 变轨"(主管派活,小兵干完后强制 Transfer 回主管)。这种需求完美契合 flowAgent 解决的痛点(交接、历史改写),因此它是 Flow 体系的直接受益者。
  2. Workflow 模式:强行复用 Flow 马甲

    • 源码证明 :在 adk/workflow.go 中,框架对所有子节点调用了 toFlowAgent,但强制加上了 WithDisallowTransferToParent()
    • 原因 :Workflow 其实不需要自由的 Transfer(它的顺序是写死的),但它需要复用 flowAgent 强大的"事件拦截与路由"能力(用来监听 BreakLoop 和 Exit)。所以它属于"阉割版复用"。
  3. Deep Agent 模式:彻底抛弃 Flow

    • 源码证明 :在 adk/prebuilt/deep/task_tool.go 中,DeepAgent 将子代理包裹时,使用的是 adk.NewAgentTool完全没有调用 SetSubAgents 或使用 flowAgent
    • 原因 :正如我们在 deep.md 中推导的,Deep Agent 走的是 "函数调用栈(Tool Call)" 路线。主 Agent 是通过同步阻塞等待 task 工具返回,而不是通过 Transfer 异步交接。既然没有交接,就不需要 flowAgent 来改写历史、不需要它来接管路由。
  4. PlanExecute 模式:彻底抛弃 Flow

    • 源码证明 :在 adk/prebuilt/planexecute/plan_execute.go 中,它直接使用了最底层的 compose.NewChain 拼接 Graph,完全没有 flowAgent 的影子。
    • 原因:PlanExecute 是一台"强管控的白盒状态机",节点只能乖乖执行图框架按数组顺序塞过来的步骤,根本没有动态路由的自由,自然不需要 Flow。

7.3 最终结论

adk/flow.go 的本质,是 利用"装饰器"抹平了多 Agent 协作中,基于 Transfer 异步交接带来的网络拓扑与历史错乱差异

在开发者眼里,无论底层是多深的网络(主管管组长,组长管小兵),最外层看起来只是一个普通的、实现了 RunResumeAgent。所有的"交接棒(Transfer)"、"历史记忆翻译"和"事件流合并",都被 flowAgent 这个包装器吃掉了。

它的代价是什么?

react.go 一样,隐含机制与约定较多。

当你的 Agent 网络出现问题(比如 A 传给 B,B 又传给 C,中间某个参数丢失了),你去查日志,会发现:

  1. 你的原始 Message 被 historyRewriter 悄悄改写了。
  2. 你的事件流被一层层的 flowAgent 包裹、过滤和复制。
  3. 如果你想实现一种并非基于 Transfer 工具的交接逻辑(比如基于外部事件触发),flowAgent 的这套体系会很难直接支持,因为它把交接逻辑深度绑定在了对 lastAction.TransferToAgent 的拦截上。

Eino 的 Flow 体系,为了提供"开箱即用的多 Agent 聊天网络",在内部做了极重的封装。它更适合构建类似 AutoGen 的聊天式 Agent 协作,但如果你的业务需要的是类似 Airflow 的 状态机工作流编排 ,用这套体系反而会感到束手束脚。


8. 进一步思考:这套模式的边界与更稳的演进

  1. 字符串改写是兜底,不是终局:当系统从 Demo 走向复杂链路,建议逐步迁移到结构化消息(保留 Author、RunPath、State、工具结果),把 "身份" 和 "状态" 从文本里抽出来。
  2. 显式 State 契约会迟早出现 :一旦链路上出现 Planner、Executor、Reviewer 等分工,纯 history 很快就不够用;越早把 State Schema 明确,越少靠约定字符串解析续命。
  3. 并发引入前先定义可重放顺序:如果要让子 Agent 并行,先定义事件排序键与重放策略,否则流式 UI、日志复现与 resume 对齐都会变成难题。
  4. 外部事件驱动需要解耦 Transfer:当交接来自外部系统(队列、Webhook、定时器)而不是模型的 tool call,交接机制需要从 "拦截 lastAction" 演进为更通用的调度接口。
相关推荐
guoji77882 小时前
2026年Gemini 3 Pro vs 豆包2.0深度评测:海外顶流与国产黑马谁更强?
大数据·人工智能·架构
NAGNIP2 小时前
一文搞懂深度学习中的损失函数设计!
人工智能·算法
千桐科技2 小时前
大模型幻觉难解?2026深度解析:知识图谱如何成为LLM落地的“刚需”与高薪新赛道
人工智能·大模型·llm·知识图谱·大模型幻觉·qknow·行业深度ai应用
Hello.Reader2 小时前
词语没有位置感?用“音乐节拍“给 Transformer 装上时钟——Positional Encoding 图解
人工智能·深度学习·transformer
我叫果冻2 小时前
ai-assist:基于 LangChain4j 的 RAG 智能助手,本地化部署更安全
人工智能·安全
Monday学长2 小时前
2026年全维度AI论文写作工具测评:基于实测数据与用户真实反馈
人工智能
Rorsion2 小时前
CNN经典神经网络架构
人工智能·深度学习·cnn
KG_LLM图谱增强大模型2 小时前
MedXIAOHE:医学多模态大模型的完整解决方案,字节跳动小荷医学推出
人工智能
天一生水water2 小时前
科研龙虾 Research-Claw 使用教程
人工智能