系统学习C++-第二十一讲-用哈希表封装 myunordered_map 和 myunordered_set

系统学习C++-第二十一讲-用哈希表封装 myunordered_map 和 myunordered_set

系统学习C++-第二十一讲-用哈希表封装 myunordered_map 和 myunordered_set

1. 源码及框架分析

SGI-STL30 版本源代码中没有 unordered_mapunordered_set ,SGI-STL30 版本是 C++11 之前的STL版本,

这两个容器是 C++11 之后才更新的。但是 SGI-STL30 实现了哈希表,只是容器的名字是 hash_maphash_set

他是作为非标准的容器出现的,非标准是指非 C++ 标准规定必须实现的,

源代码在hash_map/hash_set/stl_hash_map/stl_hash_set/stl_hashtable.h

hash_maphash_set 的实现结构框架核心部分截取出来如下:

// stl_hash_settemplate<classValue,classHashFcn= hash<Value>,classEqualKey= equal_to<Value>,classAlloc= alloc>classhash_set{private:typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht; ht rep;public:typedeftypenameht::key_type key_type;typedeftypenameht::value_type value_type;typedeftypenameht::hasher hasher;typedeftypenameht::key_equal key_equal;typedeftypenameht::const_iterator iterator;typedeftypenameht::const_iterator const_iterator; hasher hash_funct()const{return rep.hash_funct();} key_equal key_eq()const{return rep.key_eq();}};// stl_hash_maptemplate<classKey,classT,classHashFcn= hash<Key>,classEqualKey= equal_to<Key>,classAlloc= alloc>classhash_map{private:typedef hashtable<pair<const Key, T>, Key, HashFcn, select1st<pair<const Key, T>>, EqualKey, Alloc> ht; ht rep;public:typedeftypenameht::key_type key_type;typedef T data_type;typedef T mapped_type;typedeftypenameht::value_type value_type;typedeftypenameht::hasher hasher;typedeftypenameht::key_equal key_equal;typedeftypenameht::iterator iterator;typedeftypenameht::const_iterator const_iterator;};// stl_hashtable.htemplate<classValue,classKey,classHashFcn,classExtractKey,classEqualKey,classAlloc>classhashtable{public:typedef Key key_type;typedef Value value_type;typedef HashFcn hasher;typedef EqualKey key_equal;private: hasher hash; key_equal equals; ExtractKey get_key;typedef __hashtable_node<Value> node; vector<node*,Alloc> buckets; size_type num_elements;public:typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator; pair<iterator,bool>insert_unique(const value_type& obj); const_iterator find(const key_type& key)const;};template<classValue>struct__hashtable_node{ __hashtable_node* next; Value val;};
  • 这里我们就不再画图分析了,通过源码可以看到,结构上 hash_maphash_setmapset 的完全类似,复用同⼀个hashtable 实现 keykey/value 结构,hash_set 传给 hash_table 的是两个 keyhash_map 传给 hash_table 的是pair<const key, value>
  • 需要注意的是源码里面跟 map / set 源码类似,命名风格比较乱,这里比 mapset 还乱,hash_set 模板参数居然用的 Value 命名,hash_map 用的是 KeyT 命名,可见大佬有时写代码也不规范。下面我们模拟⼀份自己的出来,就按自己的风格走了。

2. 模拟实现 unordered_map 和 unordered_set

2.1 实现出复用哈希表的框架,并支持 insert

  • 参考源码框架,unordered_mapunordered_set 复用之前我们实现的哈希表。
  • 我们这里相比源码调整⼀下,key 参数就用 Kvalue 参数就用 V ,哈希表中的数据类型,我们使用 T
  • 其次跟 mapset 相比而言 unordered_mapunordered_set 的模拟实现类结构更复杂⼀点,但是大框架和思路是完全类似的。因为 HashTable 实现了泛型不知道 T 参数导致是 K ,还是 pair<K, V> ,那么 insert 内部进行插入时要用 K 对象转换成整形取模和 K 比较相等,因为 pairvalue 不参与计算取模,且默认支持的是 keyvalue ⼀起比较相等,我们需要时的任何时候只需要比较 K 对象,所以我们在 unordered_mapunordered_set 层分别实现⼀个 MapKeyOfTSetKeyOfT 的仿函数传给 HashTableKeyOfT ,然后 HashTable 中通过 KeyOfT 仿函数取出 T 类型对象中的 K 对象,再转换成整形取模和 K 比较相等,具体细节参考如下代码实现。
// MyUnorderedSet.hnamespace My_UnorderedSet {template<classK,classHash= HashFunc<K>>classunordered_set{structSetKeyOfT{const K&operator()(const K& key){return key;}};public:boolinsert(const K& key){return _ht.Insert(key);}private: hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht;};}// MyUnorderedMap.hnamespace My_UnorderedMap {template<classK,classV,classHash= HashFunc<K>>classunordered_map{structMapKeyOfT{const K&operator()(const pair<K, V>& kv){return kv.first;}};public:boolinsert(const pair<K, V>& kv){return _ht.Insert(kv);}private: hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash> _ht;};}// HashTable.htemplate<classK>structHashFunc{ size_t operator()(const K& key){return(size_t)key;}};namespace hash_bucket {template<classT>structHashNode{ T _data; HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};// 实现步骤: // 1、实现哈希表 // 2、封装unordered_map和unordered_set的框架 解决KeyOfT // 3、iterator // 4、const_iterator // 5、key不⽀持修改的问题 // 6、operator[] template<classK,classT,classKeyOfT,classHash>classHashTable{typedef HashNode<T> Node;inlineunsignedlong__stl_next_prime(unsignedlong n){staticconstint __stl_num_primes =28;staticconstunsignedlong __stl_prime_list[__stl_num_primes]={53,97,193,389,769,1543,3079,6151,12289,24593,49157,98317,196613,393241,786433,1572869,3145739,6291469,12582917,25165843,50331653,100663319,201326611,402653189,805306457,1610612741,3221225473,4294967291};constunsignedlong* first = __stl_prime_list;constunsignedlong* last = __stl_prime_list + __stl_num_primes;constunsignedlong* pos =lower_bound(first, last, n);return pos == last ?*(last -1):*pos;}public:HashTable(){ _tables.resize(__stl_next_prime(_tables.size()),nullptr);}~HashTable(){// 依次把每个桶释放 for(size_t i =0; i < _tables.size(); i++){ Node* cur = _tables[i];while(cur){ Node* next = cur->_next;delete cur; cur = next;} _tables[i]=nullptr;}}boolInsert(const T& data){ KeyOfT kot;if(Find(kot(data)))returnfalse; Hash hs; size_t hashi =hs(kot(data))% _tables.size();// 负载因⼦==1扩容 if(_n == _tables.size()){ vector<Node*>newtables(__stl_next_prime(_tables.size()),nullptr);for(size_t i =0; i < _tables.size(); i++){ Node* cur = _tables[i];while(cur){ Node* next = cur->_next;// 旧表中结点,挪动新表重新映射的位置  size_t hashi =hs(kot(cur->_data))% newtables.size();// 头插到新表  cur->_next = newtables[hashi]; newtables[hashi]= cur; cur = next;} _tables[i]=nullptr;} _tables.swap(newtables);}// 头插  Node* newnode =newNode(data); newnode->_next = _tables[hashi]; _tables[hashi]= newnode;++_n;returntrue;}private: vector<Node*> _tables;// 指针数组  size_t _n =0;// 表中存储数据个数 };}

2.2 支持 iterator 的实现

iterator 核心源代码:

template<classValue,classKey,classHashFcn,classExtractKey,classEqualKey,classAlloc>struct__hashtable_iterator{typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> hashtable;typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> const_iterator;typedef __hashtable_node<Value> node;typedef forward_iterator_tag iterator_category;typedef Value value_type; node* cur; hashtable* ht;__hashtable_iterator(node* n, hashtable* tab):cur(n),ht(tab){}__hashtable_iterator(){} reference operator*()const{return cur->val;}#ifndef__SGI_STL_NO_ARROW_OPERATOR pointer operator->()const{return&(operator*());}#endif/* __SGI_STL_NO_ARROW_OPERATOR */ iterator&operator++(); iterator operator++(int);booloperator==(const iterator& it)const{return cur == it.cur;}booloperator!=(const iterator& it)const{return cur != it.cur;}};template<classV,classK,classHF,classExK,classEqK,classA> __hashtable_iterator<V, K, HF, ExK, EqK, A>& __hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++(){const node* old = cur; cur = cur->next;if(!cur){ size_type bucket = ht->bkt_num(old->val);while(!cur &&++bucket < ht->buckets.size()) cur = ht->buckets[bucket];}return*this;}

iterator 实现思路分析:

  • iterator 实现的大框架跟 listiterator 思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意的是哈希表的迭代器是单向迭代器。
  • 这里的难点是 operator++ 的实现。iterator 中有一个指向结点的指针,如果当前桶下面还有结点,则结点的指针指向下一个结点即可。如果当前桶走完了,则需要想办法计算找到下一个桶。这里的难点是反而是结构设计的问题,参考上面的源码,我们可以看到iterator 中除了有结点的指针,还有哈希表对象的指针,这样当前桶走完了,要计算下⼀个桶就相对容易多了,用 key 值计算出当前桶位置,依次往后找下一个不为空的桶即可。
  • begin() 返回第一个桶中第一个节点指针构造的迭代器,这里 end() 返回迭代器可以用空表示。
  • unordered_setiterator 也不支持修改,我们把 unordered_set 的第⼆个模板参数改成 const K 即可,
    HashTable<K, const K, SetKeyOfT, Hash> _ht;
  • unordered_mapiterator 不支持修改 key 但是可以修改 value ,我们把 unordered_map 的第二个模板参数 pair 的第一个参数改成 const K 即可,HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
  • 支持完整的迭代器还有很多细节需要修改,具体参考下面题的代码。
在这里插入图片描述

2.3 map 支持 []

  • unordered_map 要支持 [] 主要需要修改 insert 返回值支持,修改 HashTable 中的 insert 返回值为
    pair<Iterator, bool> Insert(const T& data)
  • 有了 insert 支持 [] 实现就很简单了,具体参考下面代码实现

2.4 unordered_map 和 unordered_set 代码实现

//HashTable.h#pragmaonce#include<iostream>#include<vector>usingnamespace std;inlineunsignedlong__stl_next_prime(unsignedlong n){// Note: assumes long is at least 32 bits.staticconstint __stl_num_primes =28;staticconstunsignedlong __stl_prime_list[__stl_num_primes]={53,97,193,389,769,1543,3079,6151,12289,24593,49157,98317,196613,393241,786433,1572869,3145739,6291469,12582917,25165843,50331653,100663319,201326611,402653189,805306457,1610612741,3221225473,4294967291};constunsignedlong* first = __stl_prime_list;constunsignedlong* last = __stl_prime_list + __stl_num_primes;// >=constunsignedlong* pos =lower_bound(first, last, n);return pos == last ?*(last -1):*pos;}template<classK>structHashFunc{ size_t operator()(const K& key)const{return(size_t)key;}};// 特化template<>structHashFunc<string>{ size_t operator()(const string& key)const{ size_t hash =0;for(auto ch : key){ hash += ch; hash *=131;}return hash;}};namespace open_adrress {enumState{ EXIST, EMPTY, DELETE };template<classK,classV>structHashData{ pair<K, V> _kv; State _state = EMPTY;};//struct StringHashFunc//{// size_t operator()(const string& key) const// {// size_t hash = 0;// for (auto ch : key)// {// hash += ch;// hash *= 131;// }//// return hash;// }//};template<classK,classV,classHash= HashFunc<K>>classHashTable{public:HashTable(size_t n =__stl_next_prime(0)):_tables(n),_n(0){}boolInsert(const pair<K, V>& kv){if(Find(kv.first))returnfalse;// 扩容,负载因子==0.7就扩容if((double)_n /(double)_tables.size()>=0.7){//vector<HashData<K, V>> newtables;//newtables.resize(_tables.size() * 2);//// 遍历旧表,将旧表的数据全部重新映射到新表//for (size_t i = 0; i < _tables.size(); i++)//{// if (_tables[i]._state == EXIST)// {// //...// }//}//_tables.swap(newtables);//HashTable<K, V> newht(_tables.size() * 2); HashTable<K, V, Hash>newht(__stl_next_prime(_tables.size()+1));// 遍历旧表,将旧表的数据全部重新映射到新表for(size_t i =0; i < _tables.size(); i++){if(_tables[i]._state == EXIST){ newht.Insert(_tables[i]._kv);}} _tables.swap(newht._tables);} Hash hs; size_t hash0 =hs(kv.first)% _tables.size(); size_t hashi = hash0; size_t i =1;// 线性探测while(_tables[hashi]._state == EXIST){ hashi += i * i; i++; hashi %= _tables.size();} _tables[hashi]._kv = kv; _tables[hashi]._state = EXIST;++_n;returntrue;} HashData<K, V>*Find(const K& key){ Hash hs; size_t hash0 =hs(key)% _tables.size(); size_t hashi = hash0; size_t i =1;while(_tables[hashi]._state != EMPTY){if(_tables[hashi]._state == EXIST && _tables[hashi]._kv.first == key){return&_tables[hashi];}// 线性探测 hashi += i; i++; hashi %= _tables.size();}returnnullptr;}boolErase(const K& key){ HashData<K, V>* ret =Find(key);if(ret){ ret->_state = DELETE;--_n;returntrue;}else{returnfalse;}}private: vector<HashData<K, V>> _tables; size_t _n;// 实际存储的数据个数};voidTestHashTable1(){int a[]={19,30,5,36,13,20,21,12}; HashTable<int,int> ht;for(auto e : a){ ht.Insert({ e, e });} cout << ht.Find(20)<< endl; cout << ht.Find(30)<< endl; ht.Erase(30); cout << ht.Find(20)<< endl; cout << ht.Find(30)<< endl; ht.Insert({-3,3}); ht.Insert({13,3}); ht.Insert({33,3}); ht.Insert({323,3}); ht.Insert({23,3});for(size_t i =0; i <100; i++){ ht.Insert({rand(),i });}}structpairHash{ size_t operator()(const pair<int,int>& kv)const{ size_t hash =0; hash += kv.first; hash *=131; hash += kv.second; hash *=131; cout << hash << endl;return hash;}};// 21:07voidTestHashTable2(){//HashTable<string, string, StringHashFunc> dict; HashTable<string, string> dict; dict.Insert({"sort","排序"}); dict.Insert({"left","左边"}); dict.Insert({"sort","xxx"}); unordered_map<pair<int,int>,int, pairHash> um; um.insert({{1,3},3}); um.insert({{3,1},3});/*StringHashFunc hf; cout << hf("abcd") <<endl; cout << hf("bcad") << endl; cout << hf("aadd") << endl;*/}}namespace hash_bucket {template<classT>structHashNode{ T _data; HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};// 前置声明template<classK,classT,classKeyOfT,classHash>classHashTable;template<classK,classT,classRef,classPtr,classKeyOfT,classHash>structHTIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self; Node* _node;const HT* _pht;HTIterator(Node* node,const HT* pht):_node(node),_pht(pht){} Self&operator++(){if(_node->_next){// 当前桶还没走完 _node = _node->_next;}else// 21:05{// 当前桶走完了,需要找下一个不为空的桶里面的第一个节点 KeyOfT kot; Hash hs; size_t hashi =hs(kot(_node->_data))% _pht->_tables.size(); hashi++;while(hashi < _pht->_tables.size()){if(_pht->_tables[hashi]){ _node = _pht->_tables[hashi];break;}++hashi;}if(hashi == _pht->_tables.size()){// 所有桶都走完了,置为end() _node =nullptr;}}return*this;} Ref operator*(){return _node->_data;} Ptr operator->(){return&_node->_data;}booloperator!=(const Self& s)const{return _node != s._node;}booloperator==(const Self& s)const{return _node == s._node;}};template<classK,classT,classKeyOfT,classHash>classHashTable{// 友元声明template<classK,classT,classRef,classPtr,classKeyOfT,classHash>friendstructHTIterator;typedef HashNode<T> Node;public:typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;typedef HTIterator<K, T,const T&,const T*, KeyOfT, Hash> ConstIterator; Iterator Begin(){if(_n ==0)returnEnd();for(size_t i =0; i < _tables.size(); i++){if(_tables[i]){returnIterator(_tables[i],this);}}returnEnd();} Iterator End(){returnIterator(nullptr,this);} ConstIterator Begin()const{if(_n ==0)returnEnd();for(size_t i =0; i < _tables.size(); i++){if(_tables[i]){returnConstIterator(_tables[i],this);}}returnEnd();} ConstIterator End()const{returnConstIterator(nullptr,this);}HashTable(size_t n =__stl_next_prime(0)):_tables(n,nullptr),_n(0){}~HashTable(){for(size_t i =0; i < _tables.size(); i++){ Node* cur = _tables[i];while(cur){ Node* next = cur->_next;delete cur; cur = next;} _tables[i]=nullptr;}} pair<Iterator,bool>Insert(const T& data){ KeyOfT kot; Iterator it =Find(kot(data));if(it !=End())return{ it,false}; Hash hs;// 负载因子到1扩容if(_n == _tables.size()){//HashTable<K, V> newht(__stl_next_prime(_tables.size() + 1));//// 遍历旧表,将旧表的数据全部重新映射到新表//for (size_t i = 0; i < _tables.size(); i++)//{// Node* cur = _tables[i];// while (cur)// {// newht.Insert(cur->_kv);// cur = cur->_next;// }//}//_tables.swap(newht._tables);// 扩容 vector<Node*>newtables(__stl_next_prime(_tables.size()+1),nullptr);// 遍历旧表,将旧表的数据全部重新映射到新表for(size_t i =0; i < _tables.size(); i++){ Node* cur = _tables[i];while(cur){ Node* next = cur->_next;// cur头插到新表 size_t hashi =hs(kot(cur->_data))% newtables.size(); cur->_next = newtables[hashi]; newtables[hashi]= cur; cur = next;} _tables[i]=nullptr;} _tables.swap(newtables);} size_t hashi =hs(kot(data))% _tables.size();// 头插 Node* newnode =newNode(data); newnode->_next = _tables[hashi]; _tables[hashi]= newnode;++_n;return{Iterator(newnode,this),true};} Iterator Find(const K& key){ Hash hs; KeyOfT kot; size_t hashi =hs(key)% _tables.size(); Node* cur = _tables[hashi];while(cur){if(kot(cur->_data)== key)returnIterator(cur,this); cur = cur->_next;}returnEnd();}boolErase(const K& key){ Hash hs; size_t hashi =hs(key)% _tables.size(); KeyOfT kot; Node* prev =nullptr; Node* cur = _tables[hashi];while(cur){if(kot(cur->_data)== key){if(prev ==nullptr){ _tables[hashi]= cur->_next;}else{ prev->_next = cur->_next;}--_n;delete cur;returntrue;} prev = cur; cur = cur->_next;}returnfalse;}private: vector<Node*> _tables; size_t _n;// 实际存储有效数据个数};//unordered_set.h#include"HashTable.h"namespace My_UnorderedSet {template<classK,classHash= HashFunc<K>>classunordered_set{structSetKeyOfT{const K&operator()(const K& key){return key;}};public:typedeftypenamehash_bucket::HashTable<K,const K, SetKeyOfT, Hash>::Iterator iterator;typedeftypenamehash_bucket::HashTable<K,const K, SetKeyOfT, Hash>::ConstIterator const_iterator; iterator begin(){return _ht.Begin();} iterator end(){return _ht.End();} const_iterator begin()const{return _ht.Begin();} const_iterator end()const{return _ht.End();} pair<iterator,bool>insert(const K& key){return _ht.Insert(key);} iterator find(const K& key){return _ht.Find(key);}boolerase(const K& key){return _ht.Erase(key);}private: hash_bucket::HashTable<K,const K, SetKeyOfT, Hash> _ht;};}//unordered_map.h#include"HashTable.h"namespace My_UnorderedMap {template<classK,classV,classHash= HashFunc<K>>classunordered_map{structMapKeyOfT{const K&operator()(const pair<K, V>& kv){return kv.first;}};public:typedeftypenamehash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;typedeftypenamehash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator; iterator begin(){return _ht.Begin();} iterator end(){return _ht.End();} const_iterator begin()const{return _ht.Begin();} const_iterator end()const{return _ht.End();} pair<iterator,bool>insert(const pair<K, V>& kv){return _ht.Insert(kv);} iterator find(const K& key){return _ht.Find(key);}boolerase(const K& key){return _ht.Erase(key);} V&operator[](const K& key){ pair<iterator,bool> ret =insert({ key,V()});return ret.first->second;}private: hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};}

Read more

Python实现开源AI模型引入及测试全过程

Python实现开源AI模型引入及测试全过程

文章目录 * 摘要 * 1. 引言:开源AI生态系统概述 * 1.1 开源AI的发展现状 * 1.2 技术栈选择 * 1.3 项目目标 * 2. 环境配置与项目初始化 * 2.1 系统要求 * 2.2 创建虚拟环境 * 2.3 依赖管理文件 * 2.4 安装依赖 * 2.5 项目结构 * 3. 模型原理与架构解析 * 3.1 BERT模型原理 * 3.1.1 Transformer编码器架构 * 3.2 Hugging Face Transformers架构 * 4. 数据准备与预处理 * 4.1 数据集选择与加载

By Ne0inhk
安装openclaw时出现npm error code ENOENT npm error syscall spawn git报错的解决方案

安装openclaw时出现npm error code ENOENT npm error syscall spawn git报错的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为ZEEKLOG博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理解,而且能够帮助新手快速入门。 本文主要介绍了安装openclaw时出现npm error code ENOENT npm error syscall spawn git报错的解决方案,希望能对使用openclaw的同学们有所帮助。 文章目录 * 1. 问题描述 * 2. 解决方案 1. 问题描述 今天在使用命令安装openclaw时,却出现了npm error code ENOENT和npm error syscall spawn git的错误提示,具体报错信息如下图所示: 在经过了亲身的实践后,终于找到了解决问题的方案,最终将逐步的操作过程总结如下。希望能对遇到同样bug的同学们有所帮助。

By Ne0inhk
【2026 最新】下载安装 Git 详细教程 (Windows)

【2026 最新】下载安装 Git 详细教程 (Windows)

一、下载Git 1.下载网址:Git - Downloads (git-scm.com) https://git-scm.com/downloads 网盘链接: 通过百度网盘分享的文件:Git-2.50.1-64-bit.exe 链接:https://pan.baidu.com/s/1lRrAifTBtCYXAA4qr31UkA?pwd=dy6bhttps://pan.baidu.com/s/1lRrAifTBtCYXAA4qr31UkA?pwd=dy6b提取码:dy6b 2.等下载完成,找到下载文件的位置,双击打开安装向导 二、安装Git 1.许可声明点击Next 2.选择安装位置 记住这个位置接下来要用到 3.选择组件 勾选添加在桌面上,

By Ne0inhk
最新版 GLM-5 全栈实战全教程:从本地开源部署到 API 接入(多 Agent 架构 + 全栈编程 + 就业级项目实战)

最新版 GLM-5 全栈实战全教程:从本地开源部署到 API 接入(多 Agent 架构 + 全栈编程 + 就业级项目实战)

一、背景与技术概述 随着开源大模型技术的快速迭代,GLM-5 系列凭借优秀的指令遵循能力、长上下文支持、轻量化部署适配性与商用友好的开源协议,成为企业级AI落地与个人开发者技术进阶的核心选型之一。 本文以问题驱动为核心,完整覆盖从本地开源部署到工程化API封装、多Agent架构设计、全栈项目实战的全流程,解决开发者在大模型落地过程中面临的部署门槛高、工程化能力不足、Agent架构落地难、全栈项目缺乏可复用方案等核心痛点。本文所有实操步骤均经过生产环境验证,代码可直接复用,适配就业级项目的技术要求与企业落地标准。 1.1 GLM-5 核心技术特性 * 开源协议:Apache 2.0 协议,支持商用二次开发,无额外授权门槛 * 核心能力:支持128K超长上下文窗口,原生支持函数调用、多模态理解、结构化输出,指令遵循准确率较前代提升42% * 部署适配:原生支持FP8/INT4/AWQ/GPTQ多精度量化,最低可在16G显存环境完成流畅推理,适配消费级显卡与企业级GPU集群 * 性能优化:基于稀疏注意力架构与PagedAttention机制,推理吞吐量较同参数量模型提升3倍,

By Ne0inhk