Omni-TARS Core 组合式多智能体架构:用 @omni-tars/core 插件系统拼装 MCP / GUI / Code 全模态 Agent
这篇技术指南以 multimodal/omni-tars/core/README.md 为核心骨架,系统讲解 Omni-TARS 多模态 Agent 技术栈中 core 包的可组合(Composable)多智能体架构设计:如何基于 AgentPlugin 与 ToolCallEngineProvider 两大扩展点,将 MCP(搜索/网页)、GUI(屏幕控制)与 Code(终端/文件)三种能力以“搭积木”方式拼装成一个 Agent,并理解其系统提示词生成与生命周期 Hook 组合机制。读完本文,你将掌握 @omni-tars/core 的装配 API、插件开发范式以及底层编排源码(ComposableAgent、AgentComposer、ComposableToolCallEngine)的实现原理,可直接据此构建自己的组合式 Agent。
一、core 包在 Omni-TARS 中的定位
multimodal/omni-tars 目录下存放了一组面向多模态智能体场景的 npm 工作区包,其中:
@omni-tars/core(对应 multimodal/omni-tars/core/package.json,版本 0.3.0):提供可组合架构的基础设施——插件接口、组合式 Agent 主类、可组合 ToolCallEngine 工厂,以及三类模块化环境提示词;@omni-tars/mcp-agent:提供 MCP 插件(负责MCP_ENVIRONMENT,搜索与网页浏览能力);@omni-tars/gui-agent:提供 GUI 插件(负责COMPUTER_USE_ENVIRONMENT,GUI 交互与屏幕控制);@omni-tars/code-agent:提供 Code 插件(负责CODE_ENVIRONMENT,Bash 执行、文件编辑与 Jupyter Notebook)。
core 依赖 @tarko/agent、@tarko/agent-interface、@agent-infra/mcp-client、@agent-infra/logger、@tarko/model-provider(见 package.json 的 dependencies),其与各能力插件共同构成 Omni-TARS 开源多模态 AI Agent 技术栈中“一个 Agent 同时指挥代码、浏览器与桌面”的核心编排层。
二、架构两大设计收益:可组合性与可扩展性
🔧 Composability:按需混搭能力
README 设想的“全功能 Agent”是链式 API 形态:
// Full-featured agent
const agent = AgentBuilder.create()
.addPlugin(mcpPlugin)
.addPlugin(guiPlugin)
.addPlugin(codePlugin)
.build();
需要说明的是,从 core/src 的实际源码结构看,AgentBuilder.ts 尚未出现在核心包源码中(README 中“File Structure”一节所列的文件清单与真实目录存在出入)。当前 @omni-tars/core 的实际导出面(见 core/src/index.ts)采用的是另一套等价装配方式:ComposableAgent(构造器接收 plugins 数组)+ createComposableToolCallEngineFactory(组合多个引擎 Provider),详见下文第四节。这种“声明插件数组 → 生成组合系统提示 → 统一注册工具”的思路与 AgentBuilder 链式 API 完全同构,只是实现形态不同。如果你要按需混搭能力,请以实际导出的两个 API 为准。
📈 Extensibility:两条官方扩展路径
README 给出了两条二等公民级的扩展路径,二者分工清晰:插件负责“环境与钩子”,引擎 Provider 负责“模型输出解析策略”。
路径一:实现一个新 Agent Plugin
import { AgentPlugin } from '@omni-tars/core';
export class MyCustomPlugin implements AgentPlugin {
readonly name = 'my-custom-agent';
readonly environmentSection = '<CUSTOM_ENVIRONMENT>...</CUSTOM_ENVIRONMENT>';
async initialize(): Promise<void> {
// Initialize your plugin
}
getTools(): ToolInterface[] {
// Return tools provided by this plugin
}
// Optional lifecycle hooks
onLLMRequest?(id: string, payload: any): void | Promise<void> {}
onLLMResponse?(id: string, payload: any): void | Promise<void> {}
onEachAgentLoopStart?(): void | Promise<void> {}
onAgentLoopEnd?(): void | Promise<void> {}
}
对照真实的基类源码 core/src/AgentPlugin.ts,AgentPlugin 其实是一个带默认空实现的 TypeScript 类而非纯接口,每个字段与钩子都有确定的运行时语义:
| 成员 | 类型/签名 | 语义 |
|---|---|---|
name? |
readonly string |
插件唯一标识,会被 AgentComposer 用来做环境探测(见第五节) |
environmentSection? |
readonly string |
该插件贡献的环境能力描述段,组合时会被拼接进系统提示词 |
setAgent(agent) |
方法 | 在组合装配阶段把 Agent 实例注入插件,插件随后可通过 get agent 读取(未注入时访问会抛出 The current plugin does not associate any agent instance) |
protected tools |
Tool[] |
插件持有的工具列表 |
getTools() |
返回 Tool[] |
供组合器收集并注册到 Agent 上 |
async initialize?() |
可选 | 插件初始化,组合设置期间调用 |
onLLMRequest(id, payload) |
可选 | 每次 LLM 请求发送前触发 |
onLLMResponse(id, payload) |
可选 | 每次 LLM 响应返回后触发 |
onEachAgentLoopStart() |
可选 | 每个 Agent 循环开始时触发 |
onEachAgentLoopEnd() |
可选 | 每个 Agent 循环结束时触发 |
onAgentLoopEnd() |
可选 | 整个 Agent 循环结束时触发 |
onAfterToolCall(id, toolCall, result) |
可选 | 每次工具调用结束后触发,返回 result |
也就是说,README 中标注为可选的钩子,在基类里都以空实现方法存在,子类只需覆写关心的钩子即可,不必处理“方法不存在”的判空问题——这正是其“可叠加组合”的工程基础。
路径二:实现一个新的 ToolCallEngineProvider
export class MyCustomToolCallEngineProvider extends ToolCallEngineProvider<MyCustomToolCallEngine> {
readonly name = 'my-custom-engine';
readonly priority = 70;
readonly description = 'My custom tool call engine for specific tasks';
protected createEngine(): MyCustomToolCallEngine {
return new MyCustomToolCallEngine();
}
canHandle(context: ToolCallEngineContext): boolean {
return context.tools.some(tool =>
tool.function.name.includes('my_special_tool')
);
}
}
基类定义见 core/src/types.ts:Provider 是抽象类,通过 abstract name / priority / description 描述自己,通过 abstract createEngine() 创建引擎实例;getEngine() 内部使用单例缓存(private engineInstance)保证同一个 Provider 在组合生命周期内只实例化一个引擎。canHandle(context: ToolCallEngineContext) 是可选的路由判定函数,决定该引擎何时被选中。路由上下文 ToolCallEngineContext 包含:tools(当前可用工具)、toolCalls(模型本次输出的工具调用)、messageHistory(历史消息)与 latestAssistantMessage(本循环最新模型文本输出)。
三、工具调用引擎的组合机制
引擎选择与优先级调度
组合器核心在 core/src/ComposableToolCallEngine.ts:
- 构造函数按
priority降序排序引擎数组,engines = [...config.engines].sort((a, b) => b.priority - a.priority);defaultEngine缺省时取排序后的第一个引擎,并assert保证至少有一个可用引擎; selectEngine(context)依优先级遍历 Provider:若 Provider 未定义canHandle则直接选用;否则只有canHandle(context)返回true才选用。没有任何引擎命中时回退到defaultEngine;- 注意一个实现细节:
preparePrompt、prepareRequest与finalizeStreamProcessing走selectEngine动态路由(prepareRequest会根据tools与messageHistory选引擎),而initStreamProcessingState、processStreamingChunk、buildHistoricalAssistantMessage、buildHistoricalToolCallResultMessages固定使用defaultEngine。也就是说,流式中间态解析统一由默认引擎负责,而“给模型发什么请求、如何收尾解释最终结果”按场景动态切换; getEngineInfo()返回各引擎{ name, priority, description }清单;getActiveEngineName()通过比对单例实例找到当前活动引擎名。
ComposableToolCallEngineFactory(core/src/ComposableToolCallEngineFactory.ts)将上述组合引擎包装成与 @tarko/agent 期望的 ToolCallEngine 接口兼容的子类;而 createComposableToolCallEngineFactory(config) 是一个高阶帮助函数,它返回一个“把 config 固化进构造器”的 ToolCallEngine 构造器,从而可以直接作为 AgentOptions.toolCallEngine 传入。
原生思考控制
在 ComposableToolCallEngine.ts 中还有一个值得注意的开关:当 bypass_native_thinking(来自环境变量 NATIVE_THINKING === 'bypass',见 environments/prompt_t5.ts)为真时,prepareRequest 会向 messages 末尾推入一条空 assistant 消息,用于规避模型服务商的“自动推理”行为,保证外部可见的思维链由 Agent 端自主生成。
四、装配一个组合式 Agent
README 给出的标准装配示例:
import { codePlugin, CodeToolCallEngineProvider } from '@omni-tars/code-agent';
import { mcpPlugin, McpToolCallEngineProvider } from '@omni-tars/mcp-agent';
import { guiPlugin, GuiToolCallEngineProvider } from '@omni-tars/gui-agent';
import { ComposableAgent, createComposableToolCallEngineFactory } from '@omni-tars/core';
const toolCallEngine = createComposableToolCallEngineFactory({
engines: [
new GuiToolCallEngineProvider(),
new McpToolCallEngineProvider(),
new CodeToolCallEngineProvider(),
],
});
const agent = new ComposableAgent({
name: 'Omni Agent',
plugins: [mcpPlugin, guiPlugin, codePlugin],
toolCallEngine,
});
对照源码给出三点实操澄清(避免读者照抄示例时踩坑):
- 关于导入的细节:三个插件包的导出面已在各自
src/index.ts中得到确认——mcp-agent/src/index.ts 导出了McpAgentPlugin与McpToolCallEngineProvider;gui-agent/src/index.ts 导出了GuiAgentPlugin、GuiToolCallEngineProvider、OperatorManager;code-agent/src/index.ts 导出了CodeAgentPlugin与CodeToolCallEngineProvider。各包并未直接以具名常量codePlugin / mcpPlugin / guiPlugin导出现成插件,而是提供工厂函数或导出插件类:codePluginBuilder({ sandboxUrl, ignoreSandboxCheck })、mcpPluginBuilder({ googleMcpUrl, googleApiKey, ... })。实际写法应类似plugins: [codePluginBuilder({ sandboxUrl }), new McpAgentPlugin({...}), new GuiAgentPlugin({...})](注意 Provider 与 Plugin 需成对搭配使用,例如 GUI 场景引擎应选 GUI 专属的GUIAgentToolCallEngine)。 ComposableAgent的构造时序(见 core/src/ComposableAgent.ts):构造器先以plugins数组创建AgentComposer,再把剩余 options(...optionsWithoutPlugins)传给父类Agent,最后composer.setAgent(this)把自身注入每个插件——源码注释明确提示这样做的原因:“Remove plugins to prevent circular reference from reporting errors”(避免 options 里带插件形成循环引用)。构造完成后会打印load plugins success: [插件名列表]日志。initialize()阶段(ComposableAgent.ts):依次执行composer.initialize()(逐插件初始化)、getAllTools()收集全部插件工具并逐一registerTool,最后调用父类initialize()完成模型层就绪。
ComposableAgentOptions 即 AgentOptions & { plugins: AgentPlugin[] }(ComposableAgent.ts)。
五、模块化 Environment 与系统提示词自动生成
三个环境能力段
原文档说明:将原先单块的 environments/prompt.ts 拆分成了三个模块化环境段,各自独立导出(对应 core/src/environments 下的 code.ts、mcp.ts、computer.ts),并由 core/src/index.ts 统一 re-export:
CODE_ENVIRONMENT(code.ts)——Bash 执行、文件编辑与 Jupyter Notebook 能力。环境段内以“---- BEGIN FUNCTION/---- END FUNCTION”方式逐条声明工具:execute_bash(一次只允许执行一条命令,长驻进程需后台运行并重定向输出到日志文件,如python3 app.py > server.log 2>&1 &;可用空命令读取额外日志、用C-c中断进程)、JupyterCI(保留状态的 Python 代码沙盒)、str_replace_editor(view / create / str_replace / insert / undo_edit五种命令,str_replace要求old_str在文件中唯一匹配)。该文件还额外导出HOME_INSTRUCTION(一切 bash/文件操作必须以/home/gem为根目录、完整项目需先mkdir -p)、PROXY_INSTRUCTION(启动服务禁止占用 8080/8888/9222 等端口)与STOP_INSTRUCTION(输出</code_env>后必须立即停止,等待下一轮工具结果);MCP_ENVIRONMENT(mcp.ts)——搜索与网页浏览能力,通过 MCP 工具Search(联网搜索,复杂问题应拆解逐步搜索)、LinkReader(打开网页/PDF 等链接并按需求汇总内容)实现;COMPUTER_USE_ENVIRONMENT(computer.ts)——GUI 交互与屏幕控制能力,用于以坐标/按键/拖拽等方式操作屏幕完成界面类任务。
另外 prompt_t5.ts 面向 T5 系列 UI-TARS / Doubao 模型维护了配套的系统提示分组 SYSTEM_PROMPT_GROUP、createSystemPromptGroup 以及 think_token(默认 thinkt,可用环境变量 THINK_TOKEN 覆盖);code_functions / mcp_functions / gui_functions 三组变量以 JSON 格式声明了对应工具集。
组合器如何拼接系统提示词
AgentComposer(core/src/AgentComposer.ts)是“把 N 个插件变成一份系统提示词”的关键:
generateSystemPrompt()(L44-L64)输出 = 基础提示(“You are a general AI agent … can interact with the following environments:{envs}… 推理过程需包裹在<think></think>中”)+ 拼接所有插件提供的environmentSection(过滤空段后以空行连接)+ 由generateUsageInstructions()生成的使用指令;getAvailableEnvironments()通过hasPlugin('code' / 'mcp' / 'computer' / 'gui')判断当前可用环境,并在hasPlugin中用plugin.name.toLowerCase().includes(type)按插件名字符串子串匹配(源码中留有 TODO 注释,说明该方案后续需优化);generateUsageInstructions()(L169-L221)会基于可用插件动态渲染一段<IMPORTANT_NOTE>:要求模型推理结束</think>后用<环境名>…</环境名>标签声明下一步要使用的环境,并分别给出三类环境的输出范本——代码环境用<code_env>+<function=…>/<parameter=…>的 XML 式调用;MCP 环境用<mcp_env>+<|FunctionCallBegin|>[…]<|FunctionCallEnd|>的 JSON 调用;计算机环境用<computer_env>+Action: click(point='<point>100 200</point>')之类的动作指令;完成任务则以<answer>…</answer>提交最终答案。也就是说,“能力是否出现在系统提示词里,完全取决于你拼装了哪些插件”,这正是模块化架构的直观体现。
六、Hook 组合系统:跨插件叠加的生命周期
README 将其列为组合系统的核心特性——生命周期钩子可以在多个插件间可加性地分层叠加。其分发实现位于 ComposableAgent 与 AgentComposer 的配合:
ComposableAgent覆写了父类的全部钩子(ComposableAgent.ts):onLLMRequest、onLLMResponse、onEachAgentLoopStart、onEachAgentLoopEnd、onAgentLoopEnd、onAfterToolCall,每个覆写方法内部只做一件事——把控制权转交给composer.executeXxx(),最后onAgentLoopEnd仍会调用父类实现以确保 Agent 循环正常终结;AgentComposer对每种钩子提供executeXxx()批处理方法,内部按 plugins 数组顺序for...of依次 await(例如 executeOnLLMRequest),executeOnAfterToolCall在全部执行后返回原始result(L131-L143),使插件可以“先看后改/透传结果”;initialize()在逐个插件初始化时会统计并打印每个插件的耗时日志。
一句话总结运行时序:Agent 主循环每触发一次生命周期事件 → ComposableAgent 钩子 → AgentComposer.executeXxx → 顺序遍历所有插件钩子。每个插件只需关心自己的钩子逻辑,多个插件对同一事件的观察与副作用天然叠加。
七、官方内置插件包速览
README 的“What Was Implemented”部分描述了三个官方插件与一个复合形态,结合源码可进一步补充其实用配置:
@omni-tars/mcp-agent(搜索/网页能力)
McpAgentPlugin 基于新核心架构接管 MCP_ENVIRONMENT,支持通过配置挂载 mcpServers。从 mcp-agent/src/index.ts 的 mcpPluginBuilder 可见其典型服务描述格式:
{
type: 'streamable-http',
name: McpManager.McpClientType.Google,
description: 'google search tool',
url: option.googleMcpUrl,
headers: { 'x-serper-api-key': option.googleApiKey },
enable: true,
}
需要传入的 MCPTarsExtraOption 包括:googleMcpUrl、googleApiKey(必填)与 tavilyApiKey、linkReaderMcpUrl、linkReaderAK(可选,enable 字段随 key 是否存在而置位),MCP 客户端能力来自 @agent-infra/mcp-client 依赖。
@omni-tars/gui-agent(GUI / 屏幕能力)
GuiAgentPlugin 接管 COMPUTER_USE_ENVIRONMENT,可配置屏幕尺寸与动作预算,面向 computer-use 工具就绪。GUIAgentConfig<TOperator>(gui-agent/src/index.ts)要求提供 operator(如经 OperatorManager.create(agentMode, sandboxUrl) 创建,源码位置 gui-agent/src/OperatorManager.ts)与模型配置 model.baseURL / id / apiKey / uiTarsVersion;可选 systemPrompt、signal(AbortSignal,用于取消)、maxLoopCount、loopIntervalInMs。uiTarsVersion 支持 'ui-tars-1.0' | 'ui-tars-1.5' | 'doubao-1.5-ui-tars-15b' | 'doubao-1.5-ui-tars-20b'。核心 AgentMode 类型(core/src/types.ts)规定 id 取值为 'omni' | 'gui' | 'game',并带可选的 browserMode: 'dom' | 'visual-grounding' | 'hybrid'。
@omni-tars/code-agent(代码执行能力)
CodeAgentPlugin 接管 CODE_ENVIRONMENT,可配置工作目录与执行限制,面向 bash、文件编辑与 Jupyter 工具就绪。CodeAgentExtraOption(code-agent/src/index.ts)包含 sandboxUrl(必填的代码沙箱地址)与 ignoreSandboxCheck(可选,跳过沙箱连通性校验)。
@omni-tars/agent(MCP + GUI + Code 复合体)
三者之上还存在一个将 MCP、GUI、Code 能力合一的聚合形态,即开篇 ComposableAgent({ name: 'Omni Agent', plugins: [mcpPlugin, guiPlugin, codePlugin], toolCallEngine }) 描述的组合体——README 仅以一句收尾,而其工程化落地正是第四节“三种能力拼装进一个 Agent”的完整示例。
八、工程结构地图与阅读指引
README 自带的目录结构(multimodal/omni-tars/README.md 的同目录文档树)与实际仓库核对后基本吻合,但需注意两处差异:core/src 下并未实际存在 AgentBuilder.ts,取而代之的是 AgentPlugin.ts、ComposableToolCallEngine.ts、ComposableToolCallEngineFactory.ts 与 plugins/snapshot.ts 等文件(后者通过 core/src/index.ts 导出的 SnapshotPlugin 提供快照能力)。建议按下面的“真实结构 + 阅读顺序”逐层深入:
multimodal/omni-tars/
├── core/ # 可组合架构(本文核心)
│ ├── src/
│ │ ├── index.ts # 公共导出面(推荐入口)
│ │ ├── AgentPlugin.ts # 插件基类:环境段 + 生命周期钩子 + 工具
│ │ ├── ComposableAgent.ts # 组合式 Agent:初始化、钩子分发、工具注册
│ │ ├── AgentComposer.ts # 系统提示词组合器 + execute* 钩子批处理
│ │ ├── ComposableToolCallEngine.ts # 引擎选择与优先级路由
│ │ ├── ComposableToolCallEngineFactory.ts # 兼容 @tarko/agent 的构造器包装
│ │ ├── types.ts # ToolCallEngineProvider / AgentMode 等类型
│ │ ├── environments/ # code.ts / mcp.ts / computer.ts / prompt_t5.ts
│ │ ├── plugins/snapshot.ts # SnapshotPlugin
│ │ └── utils/parser.ts + streamingParser*/ # 流式结果与内容解析器
│ ├── examples/openai.ts # 使用示例
│ └── test/ # ComposableToolCallEngine.test.ts / parser 测试
├── mcp-agent/src/ # McpAgentPlugin、McpToolCallEngine(Provider)、mcpServers 挂载
├── gui-agent/src/ # GuiAgentPlugin、GUIAgentToolCallEngine、OperatorManager
└── code-agent/src/ # CodeAgentPlugin、CodeToolCallEngine(Provider)
深入验证建议优先阅读这几处:想看“组合引擎如何路由”,读 test/ComposableToolCallEngine.test.ts;想看“模型输出如何被解析成工具调用/内容”,读 core/src/utils/parser.ts 及其 parseCodeContent / parseComputerContent / parseMcpContent 三个导出(分别面向三类环境的输出格式,配套 test/parseRealCode.test.ts 与 test/parser.test.ts);想跑通构建与测试,可在包目录执行 rslib build 与 vitest(脚本定义见 core/package.json)。
九、小结
Omni-TARS core 包通过一套“插件声明能力、Provider 声明解析策略、组合器负责拼接”的三层设计,把多模态 Agent 拆成了高度内聚、可独立演进的积木:AgentPlugin 提供环境说明与生命周期钩子,ToolCallEngineProvider 提供按优先级与上下文选择的工具调用解析引擎,ComposableToolCallEngine/工厂把多个 Provider 编排为一个整体,而 AgentComposer 则把任意插件集合实时编译成一套自洽的系统提示词。MCP、GUI、Code 三个官方插件包正是这套架构的第一批落地案例——理解 core 的组合规则,你既能直接拼装出“全模态 Omni Agent”,也能以同样模式扩展出自己的环境插件与解析引擎。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.24 K638- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python670
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#230
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python52874
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go22545
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java36351