毕业实习-2026:搭建多智能体系统后端服务

  • 实习地点: 天融信成都研发中心
  • 实习时间: 2026/6/1 - 2026/6/26 工作日朝九晚五

课程设计——安全体系规划方向

天融信 · TOPSEC

安全体系规划方向项目列表

序号项目名称技术方向
1XX省医疗保障信息平台安全建设体系规划等级2.0 + 云安全
2XX商业银行数据安全防护体系规划等级2.0 + 数据安全
3XX水净化厂工控安全体系建设规划等级2.0 + 工控安全
4XXX城市管理局商用密码应用体系建设规划等级2.0 + 商用密码

课程设计——软件开发

AI Agent 专项开发

需求描述:

掌握基于 Golang 和 Eino 框架的现代 AI Agent 开发能力,专注于 Multi-Agent 协作与图编排。

说明:

  1. 参考 DeferFlow 2.0、OpenManus / OpenClaw 进行开发。
  2. 最终产出:一套具备自主规划能力的 Agent 后端服务。

基于 Eino 框架的智能告警分析 AI 智能体系统

需求描述:

使用 CloudWeGo Eino 框架,开发一个多 Agent 协作的 AI 智能体系统,实现企业安全告警的智能分析处理。

说明:

开发一个 AI 智能体系统,让多个专业 Agent 协作分析告警:

  • 自动过滤无意义的告警,合并相似的重复告警
  • 识别高风险的操作链(如:复制 → 压缩 → 外发)

如何撰写安全规划

首先要对整个项目进行一个阐述,为什么要做这个项目,方案编制依据,(适用范围),建设原则。
构建安全技术保障体系,构建安全管理保障体系。
建设总体设计思路——总体拓扑图,方案说明
对图中主要产品部署情况进行说明

基本要求和通用技术要求

Eino库结构概述

智能体是一个封装了完整的商业逻辑并可以直接运行的AI应用,组件在内部运行。(ChatModel与Tool)——(上下文与执行)。
为了完成这样的系统,需要相应的算法和数据结构。组件(ChatModel/Tool)就是对功能的抽象,或者说是对算法的抽象,(schema.AgenticMessage)实现对话的数据结构。

除此之外,为了帮我们管理这些对话的数据结构,比如说处理流式输出,实现终端与恢复。调用工具还是模型的流编排……Adk,还提供了Agent,也就是完整的运行时框架,例如ChatModelAgent。

以上内容就是Eino framework layer,提供基础的抽象。但不关心业务流程,Bussiness layer完成对内存及会话的管理,工具与文件系统访问,中间件以及回调与追踪,终端恢复,图工具……

Eino通过利用不同的数据结构定义和设计组件间的接口,实现完整的系统。

组件在Eino中表示可替换的,可组合的能力单元,举例:

  • ChatModel: 调用大语言模型
  • Tool: 执行具体的任务
  • Retriever:获取信息
  • Loader:加载数据

Eino 定义一组组件接口( ChatModel, Tool, Retriever, Loader, etc.),每一个描述了一类可替换的能力:

1
2
3
4
5
type BaseModel[M any] interface {
Generate()
Stream()
}
type AgenticModel = BaseModel[*shcema.AgenticMessage]

ChatModel

最常见且基础的组件,核心接口:Generator()/Stream(),直接返回消息内容,用于纯LLM的单轮次对话互动。

shcema.AgenticMessage: 对话的基本单元


在单个的模型调用中,模型可能返回多个指令事件——例如,首先输出reasoning,然后调用服务器工具,以及更多的reasoning,然后掉哟个函数工具。AgenticMessage使用ContentBlock按顺序存储了这些结构化的事件。

Agent接口

Agent是ADK的核心接口,定义了智能智能体的具体行为:

1
2
3
4
5
6
7
8
9
type Agent interface {
Name(ctx context.Context) String
Description(ctx context.Context) string

Run(ctx context.Context, input *AgentInput, options ..AgentRunOption) *AsyncIterator[*AgentEvent]
}
// Nmae()/Description(): 识别Agent的名字和描述
// Run():执行智能体的主要方法,接收输入消息并返回事件流
//

ChatModelAgent

对Agent接口的实现,在ChatModel之上构建:

1
2
3
4
5
6
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "ch",
Description: "A minimal",
Instrution: instruction,
Model: cm, // !
})

ChatModelAgent是一种组件,而Agent是可以包装多个组件的高层抽象。相较于ChatModel,ChatModel的职责是与LLM通信,实现多个模型提供者的多态,属于底层能力支持。

Runner

Runner是执行一个Agent的入口,不属于作为能力单元的组件,而是执行管理工具,负责管理Agent的生命周期:

1
2
3
4
5
type Runner struct {
a Gent
enableStreaming bool
store CheckPointStore
}
1
2
3
4
5
6
runner := adk.NewTypedRunner[M](adk.TypedRunnerConfig[M]{
Agent: agent,
EnableStreaming: true,
})
events := runner.Run(ctx,history)
events := runner:Query(ctx,"hello")

AgentEvent & AsyncIterator

作为由Runner返回的事件单元,AsyncIterator是一个非阻塞的流式迭代器。AsyncIterator让你能够实时地消耗事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
events := runner.Run(ctx,history)

for {
event , ok := events.Next()
if !ok{
break
}
if event.Err != nil {

}
if event.Output != nil && event.Output.MessageOutput != nil {

}
}

实现多轮对话

多轮对话通过维护在调用方的历史实现:

  • 使用history []M积累对话消息
  • 每个用户输入:通过msgops.NewUser[M]加入到历史
  • 调用runner.Run(ctx, msgops.NormalizeMessagesForModelInput(history))获得事件流并消耗辅助文本
  • 通过msgops.NewAssistant[M]把辅助文本添加回历史,然后进入下一轮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func runTyped[M adk.MessageType](ctx context.Context, instruction string) {
agent, err := adk.NewTypedChatModelAgent[M](ctx,&adk.TypedChatModelAgentConfig[M]{
Name: "Ch02Agent",
Instruction: instruction,
Model: cm,
})
if err != nil {
log.Fatal(err)
}
runner := adk.NewTypedRunner[M](adk.TypedRunnerConfig[M]{
Agent: agent,
EnableStreaming: true,
})
history := make([]M,0,16)
for {
line := readUserInput()
if line == ""{
break
}
history = append(history,msgops.NewUser[M](line))
events := runner.Run(ctx,msgops.NormalizeMessagesForModelInput(history))
result, err := helpers.PrintAndCollect[M](events, helpers.PrintOptions{})
if err != nil {
log.Fatal(err)
}
history = append(history, msgops.NewAssistant[M](result.AssistantText, nil))
}
}

内存与会话(持久对话)

这章节的目标:持久对话历史以及跨进程的会话恢复。
内存的职责:

  • 内存是对话历史的持久存储:把对话存储到数据库或磁盘
  • 内存支持会话管理:每个会话代表了一个完整的对话
  • 内存与智能体解耦:智能体不关心存储细节,只关心消息列表

Session(业务层概念)

Session代表一个完整的对话。

1
2
3
4
5
6
7
type Session struct {
ID string,
CreatedAt time.Time

messages []M // Conversation history; in this example M defaults to *schema.AgentMessage
// ...
}
  • Append(msg):给session添加一个消息并持久化保存
  • GetMessages():获取所有的消息
  • Title():从第一个用户消息生成一个会话标题

Store(业务层概念)

Store为多个Session管理持久的存储。

1
2
3
4
type Store struct {
dir string
cache map[string]*Session
}
  • GetOrCreate(id):获取或创建一个Session
  • List():列出所有的Session
  • Delete(id):删除一个Session

JSONL文件格式

每个会话用一个.jsonl文件存储:

1
{"type":"session","id":"xxx","created_at":"xxx","message_kind":"agentic"}

会话默认存储在./data/session_agentic;设置SESSION_DIR_AGENTIC以使用不同的目录。

内存实现

距离一个使用JSONL文件用于会话历史存储。

  • Create the Store
    1
    2
    3
    4
    5
    sessionDir := "./data/sessoin_agentic"
    store, err := mem.NewStore(sessionDir)
    if err != nil {
    log.Fatal(err)
    }
  • Get or Create a Session
    1
    2
    3
    4
    5
    sessionID := "xxx"
    session, err := store.GetOrCreate(sessionID)
    if err != nil {
    log.Fatal(err)
    }
  • Append User Message
    1
    2
    3
    4
    userMsg := msgops.NewUser[M]("Hello")
    if err := session.Append(userMsg); err != nil{
    log.Fatal(err)
    }
  • Get History and Call the Agent
  • Append Assistant Message
1
2
3
4
5
6
histor := session.GetMessages()
events := runner.Run(ctx, msgops.NormalizeMessagesForModelInput(history))
result, err != helpers.PrintAndCollect[M](events, helpers.PrintOptions())
if err != nil {
log.Fatal(err)
}

Seesion与Agent的关系:业务层以及框架层协同

  • Session是一个业务层概念:由业务代码实现并管理,对于存储和加载对话历史负责
  • Agent(Runner)是一个框架层概念:由Eino框架提供,负责处理信息并生成回复
  • 它们的交互点:业务层通过session.GetMessages()获取消息列表,然后通过msgops.NormalizeMessagesForModelInput(history)生成模型输入,最终传递给runner.Run(ctx,messages)用于处理。

工具与文件系统的访问

Agent可以访问文件系统的工具能力
Agent limitatoins:

  • 仅能生成文字回复
  • 无法访问外部资源
  • 无法执行真实的工作
    Tool的职责
  • Tool是一个用于Agent的能力扩展
  • Tool封装了具体的实现: Agent只需要关注输入和输出
  • Tool是可组合的:Agent可以有多个工具,选择所需的
1
2
3
4
5
6
7
8
9
10
11
type BaseTool interface {
Info(ctx context.Context) (*schema.ToolInfo, error)
}
type InvokableTool interface {
BaseTool
InvokableRun(ctx context.Context, argumentsInJSON string, opts ..Option)(string, error)
}
type StreamableTool interface {
BaseTool
StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option)(*schema.StreamReader[string],error)
}

接口架构:

  • BaseTool:基础接口,只提供元数据
  • InvokableTool:执行工具(扩展BaseTool)
  • StreamableTool:流式工具(扩展BaseTool)

Backend是Eino用于文件系统的抽象接口:

1
2
3
4
5
6
7
8
type Backend interface {
LsInfo(ctx context.Context, req *LsInfoRequest)([]FileInfo, error)
Read(ctx context.Context, req *ReadRequest) (*FileContent, error)
GrepRaw(ctx context.Context, req *GrepRequest)([]GrepMatch, error)
GlobInfo(ctx context.Context, req *GlobalInfoRequest)([]FileInfo, error)
Write(ctx context.Context, req *WriteRequest) error
Edit(ctx context.Context, req *EditRequest)
}

LocalBackend是本地文件系统对Backend的实现,直接访问OS文件系统:

1
2
import localbk "github.com/cloudwego/eino-ext/adk/backend/local"
backend, err := localbk.NewBackend(ctx, &localbk.Config{})
  • 直接使用GO标准库访问本地文件系统
  • 支持所有的后台接口方法
  • 支持执行终端命令(ExecuteStreaming)
  • 路径安全:要求绝对路径以防止目录穿越攻击
  • 零配置

中间件(横切关注点)

  • 中间件是智能体的拦截器:在调用前后插入自定义逻辑
  • 中间件可以处理错误:将错误转换为模型能够理解的格式
  • 中间件可以实现重试:自动重试失败的操作
  • 中间件是可组合的:多个中间件可以链式串联在一起

中间件的执行顺序

处理器(即中间件)按照数组顺序进行包装,形成洋葱模型:

1
2
3
4
5
Handlers: []adk.ChatModelAgentMiddleware{
&middlewareA{}, // 最外层:最先包装,最先拦截请求,但 WrapModel 最后生效
&middlewareB{}, // 中间层
&middlewareC{}, // 最内层:最后包装
}

使用建议:把safeToolMiddleware放在最后边,让由其它中间件丢出的中断错误正确向后传播。

SafeToolMiddleware

SafeToolMiddleware把工具错误转换成一个字符串,这样模型可以理解并处理它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type safeToolMiddleware struct{
*adk.BaseChatModelAgentMiddleware
}
func (m *safeToolMiddlwware) WrapInvokabeToolCall(
_ context.Context,
endpoint adk.InvokableToolCallEndpoint,
_ *adk.ToolContext,
) (adk.InvokableToolCallEndpoint, error) {
return func(ctx context.Context, args string, opts ...tool.Option) (string, error) {
result , err := endpoint(ctx, args, opts...)
if err != nil {
if _, ok := compose.IsInterrupRerunError(err); ok{
return "", err
}
return fmt.Sprintf("[tool error] %v", err) ,nil
}
return result, nil
},nil
}

回调和追踪

这章的目的是理解回调机制以及用于追踪和观察的集成 CozeLoop。
以下是您提供的英文内容的完整中文翻译:

  • Callback 是 Eino 的旁路机制:从 component 到 compose(下文详谈)到 adk,一以贯之
  • Callback 在固定点位触发:组件生命周期的 5 个关键时机
  • Callback 可抽取实时信息:输入、输出、错误、流式数据等
  • Callback 用途广泛:观测、日志、指标、追踪、调试、审计等

    Handler是Eino中定义回调处理器的核心接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type Handler interface {
    OnStart(ctx context.Context, info *RunInfo, input CallbackInput) context.Context

    OnEnd(ctx context.Context, info *RunInfo, output CallbackOutput)
    context.Context

    OnError(ctx context.Context, info *RunInfo, input *schema.StreamReader[CallbackInput]) context.Context

    OnEndWithStreamOutput(ctx context.Content, info *RunInfo,
    out *schema.StreamReader[CallbackOutput]) context.Context
    }
    • 状态转移:统一Handler的OnStart->OnEnd可通过context传递状态
    • 性能优化:实现TimingChecker接口可跳过不需要的时机
    • 全流程覆盖:从component到compose到adk,所有组件都支持
    • 旁路机制:不干扰主流程
    1
    2
    3
    4
    5
    type RunInfo struct {
    Name string
    Type string
    Component string
    }

CozeLoop

字节提供AI应用的可观测性平台,提供:
完整调用链路可视化,延迟、Token消耗、错误率,几种管理所有日志,在线查看和调试。

自定义Callback Handler

1
2
3
4
5
6
7
8
handler := callbacks.NewHandlerHelper().
OnStart(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
log.Printf("[trace] %s%s start",info.Component, info.Name)
return ctx
}).
OnEnd ...
.OnError.
Handler()

中断与恢复

Interrupt是Agent的暂停机制,在关键操作前暂停,等待用户确认,且Interrupt可携带信息,像用户展示即将执行的操作,并能够恢复。

1
2
3
4
5
6
7
8
9
func myTool(ctx, args):
if first call:
save args
return interrupt
else:
if approve:
return execute
else
return "operation rejected"
  • 配置Runner使用CheckPointStore
    1
    2
    3
    4
    5
    runner := adk.NewTypedRunner[M](adk.TypedRunnerConfig[M]{
    Agent: agent,
    EnableStreaming: true,
    CheckPointStore: adkstore.NewInMemoryStore(),
    })
  • 配置Agent使用ApprovalMiddleware
    1
    2
    3
    4
    5
    6
    agent, err := deep.NewTyped[M](ctx, &deep.TypedConfig[M]{
    Handlers: []adk.TypedChatModelAgentMiddleware[M]{
    newApprovalMiddleware[M](),
    newSafeToolMiddleware[M](),
    }
    })
  • 处理中断事件

Graph Tool 复杂工作流

简单Tool具有这样的局限:

  • 单一职责:每个Tool只做一件事
  • 无法并行,多个独立任务无法同时执行
  • 难以复用

从更大的视角看,Eino的compose包提供了非常通用、确定性的编排能力。Eino的compose包提供了非常通用、确定性的编排能力:呢可以把任何需要“确定性业务流程”的系统,用compose的Graph/Chain/Workflow组织成可执行的流水线,并且它能够原生编排Eino的所有component,同时具备完整的callback体系,以及interrupt/resume + checkpoint支持。

Graph Tool的定位:

  • Graph Tool是compose工作流的Tool化封装:把compose.Graph/ compose.Chain / compose.Workflow这类可编译的编排产物,包装成一个Agnet可调用的Tool
  • 支持并行/分支/组合,Graph Tool把compose提供的(并行、分支、字段映射、子图等),暴露为Tool入口。
  • 支持状态管理与持久化,通过checkpoint保存/恢复运行状态
  • 可中断恢复

Skill Middleware

引入skill技能包,采用skill middleware注入和管理skills,让Agent可以发现并加载一组可复用的技能文档,(skill.md),在需要时通过工具调用使用它们(渐进式披露)。

  • 用本地filesystem backend提供文件读取/Glob能力
  • 用skill.NewBackendFromFilesystem把EINO_EXT_SKILLS_DIR变成一个Skill Backend
  • 用skill.NewTyped[M]生成泛型Skill middleware,塞进Handlers

毕业实习-2026:搭建多智能体系统后端服务
https://47.108.189.123/2026/06/01/实习经历/毕业实习-2026/TopSec/
Author
Dong
Posted on
June 1, 2026
Licensed under