资讯动态

用Agent给AI芯片软件栈绘制可导航的全栈地图

发布时间:2026/10/4 11:23:24 来源:尧图企业网站定制
做AI芯片的朋友基本都有同感硬件一旦点亮真正的长跑才开始。一块芯片从流片回来到能跑出像样的性能中间隔着一整套软件栈——驱动、运行时、编译器、算子库、框架适配、性能工具每一层都是人堆出来的。最难受的不是哪一层难写而是整个栈太庞大、太分散团队里没有一个人能讲清楚“全貌”。这篇文章是系列的第2篇主题就是用Agent给一块AI芯片画一张“全栈软件地图”把所有零散的技术文档、源码仓库、编译脚本、依赖关系变成一张可导航、可检索、可持续更新的结构化图谱。我最近用这个思路整理了一个项目里的AI加速卡软件栈效果比预期好不少。这篇文章就把完整的方法、工作流、代码示例和我踩过的坑都写出来。适合正在做AI芯片软件栈、维护异构计算平台或者想用Agent做技术知识库建设的人参考。1. 先搞清楚AI芯片的全栈软件到底分几层1.1 六层软件栈每一层都不能缺做芯片软件的地图第一步不是急着写代码而是先定义分层标准。我实际梳理下来AI芯片的软件栈可以稳定地划分成六层几乎适配所有面向深度学习加速的处理器L0 硬件抽象层寄存器映射、DMA通道、片上缓存、SRAM分区、多核间中断这层直接对着硬件文档写。L1 固件与驱动层Boot ROM、固件升级、内核态驱动KMD、用户态驱动UMD、设备初始化、中断处理。L2 运行时与资源管理层context管理、内存池分配、stream/队列调度、同步原语、资源隔离。L3 编译与算子层基于LLVM/MLIR的编译器、Triton/TVM这类编译调度框架、手写算子库和cuDNN类似物。L4 框架适配层PyTorch的后端扩展、ONNX Runtime执行器、TensorFlow插件、量化推理引擎、GGML这类轻量推理框架的适配。L5 工具链与生态层性能分析器profiler、调试器、模型转换工具、镜像烧录工具、CI测试框架。这张表不是我拍脑袋定的而是参考了当前主流AI芯片软件栈的共同结构。无论芯片走GPGPU路线还是ASIC路线最终都会收敛到这六层只是每层的厚度和实现方式不同。有了分层标准整张地图就有了骨架。Agent后续的所有工作本质上就是把这六层填上具体的模块名、文件路径和依赖关系所以这个标准必须在一开始就定清楚否则后续各个环节都会乱。1.2 没有地图的时候团队是怎么受苦的我见过太多团队在这种软件栈里挣扎核心痛点其实就三个。第一新人上手周期极长。一个新同事分到编译器组想看一个算子schedule是怎么注册进后端的他得自己从Git仓库里翻出来然后顺着代码一层层往上看至少要一两个月才能真正搞清楚周边模块的关系。没有人能给他一张带依赖关系的全图。第二问题定位靠人肉搜索。线上跑模型推理变慢了大概率是算子走错了路径但究竟是驱动层没选对内存池还是runtime没有走异步复制还是算子库的kernel只实现了naive版本这个排查过程在传统模式下只能靠老员工的经验。团队里没有一个系统能告诉你“从算子入口到硬件寄存器”的完整调用链。第三跨模块协作靠开会。编译器组改了一个接口签名runtime组和算子库组都得跟着改但到底谁受影响、影响面多大经常是发完邮件才知道。这就是典型的依赖关系不透明。地图解决的就是这个问题它是“导航依赖关系知识索引”三合一的载体。不是画一张静态框图给人看看而是要让每个模块、每条依赖都能被检索、被追问、被自动更新。2. 为什么这件事适合交给Agent去做2.1 传统梳理方式慢在哪早几年没有Agent的概念我们做过一次类似的软件栈梳理用的是笨办法几个核心工程师开会分区块然后各自去翻文档和源码再用draw.io画框图。一个六层软件栈拆成几十个模块光信息收集就花了三周画图又花了一周等到代码更新了一批接口这张图已经过时了。也试过更自动化的手段写爬虫抓文档、用脚本扫include头文件、把Makefile里的target关系抽出来。这套方案确实快但有两个致命问题一是只能发现文件层面的机械联系无法理解模块的“职责语义”比如一个路径叫dma_manager的目录脚本根本不知道它是驱动层的还是runtime层的二是因为缺少理解能力最终产出的不是“地图”而死“文件关系网”密到没法看。2.2 Agent做这件事的核心优势Agent和脚本最大的差别在于意图理解与任务拆解。同样是扫描源码脚本只能按关键词匹配Agent可以根据上下文判断一个仓库目录在不同SDK版本中的角色变化同样是解析文档脚本只能做全文检索Agent可以跨文档地去归纳同一个硬件特性在不同手册中的描写差异。我实际用下来Agent的优势体现在四个具体方面任务拆解把“生成一张全栈软件地图”这个大目标拆成“资料采集、分层归类、依赖建模、地图渲染、校验修正”五个子任务每个子任务可以被不同的Agent或同一Agent分阶段执行形成清晰的作业流。上下文关联普通搜索只能告诉你“这个文档里有DMAReset这个词”Agent能把驱动代码里的DMAReset、TRM手册里的寄存器描述、runtime里调用DMAReset的代码段串成一条“为什么这里要reset、依赖什么条件”的完整链路。迭代修正Agent可以保留长期记忆和短期记忆。第一轮把某个模块分错了类你纠正之后它会把修正写入记忆下一轮遇到类似模块就不会再犯。这一点是纯脚本完全做不到的。工具调用Agent不只是“会说话”它还能调Shell命令、跑Python脚本、查询数据库、写文件。也就是说它既能思考软件栈的分层逻辑也能实际操作代码仓库去验证某个依赖关系是否存在。2.3 Agent不是魔法核心在于“框架技能”的编排有人听到“用Agent做地图”还以为是一个对话框吃进全部资料然后吐出一张图。实操中远远不是这样。真正稳定可靠的做法是搭一个Agent框架里面编排好几类角色一个负责全局拆解的规划Agent一个或多个负责具体扫描和归纳的执行Agent再来一个专门做校验的Agent。这就是现在大家常说的“Agent框架与编排”再加上“Agent记忆”能力和“Agent skill”能力才形成一个完整体系。以我这次使用的轻量编排方式为例主Agent负责任务规划子Agent分别负责“文档阅读与归纳”和“代码静态扫描”校验Agent的任务是发现前后矛盾。主Agent不会直接去读全部原始资料而是分配任务、汇总结果、检查一致性。这样做的最大好处是上下文可控每个子Agent的输入都是我按需喂进去的资料切片不会因为某个会话塞入过多样例而溢出。再强调一点Agent的能力边界取决于你给它的工具和知识范围。如果只给一个纯语言模型不加任何检索工具和代码执行工具它生成的地图大概率是错的。所以这篇文章后面所有流程都默认你给Agent配了文件读取、代码检索、Python执行这三样基础工具。3. Agent工作流设计从资料到地图的五步走3.1 第一步资料采集与清洗所有地图都建立真实资料之上。资料的完整度直接决定地图质量这里不能省。我按以下清单采集素材芯片技术参考手册地址映射、寄存器描述、中断、启动流程软件仓库源码驱动、runtime、编译器、算子库、工具链分仓库拉取官方文档和开发者指南安装文档、API参考、架构说明Release notes和版本更新记录指向接口的变化和历史演进构建脚本和CI配置CMakeLists.txt、Makefile、Dockerfile、YAML流水线已有Wiki、注释文档、测试用例测试用例特别重要它往往揭示了模块之间真实的调用方式采集后不能直接扔给Agent先做清洗。我常用的策略是先按路径后缀和文件类型做一轮粗过滤把二进制、第三方缓存、node_modules这类无关文件剔掉再把剩余文件按“驱动类源码、运行时类源码、编译类源码、工具类源码、文档类资料”分桶。这个粗分类不需要Agent参与用一段Python脚本就能完成速度极快。3.2 第二步分层结构抽取清洗完的资料开始进入Agent的主场。我把这批资料按桶输入给执行Agent每个桶对应一批任务每个任务的Prompt必须含以下要素当前芯片的软件栈分层定义把六层标准的描述粘贴进去待分析的文件路径清单和文件树输出格式要求严格的JSON Schema每条判断要求给出“依据证据”比如“为什么把dma_manager归到驱动层因为代码中有mmap回调函数注册在file_operations结构体里”我用的Prompt模板大致是这样你将分析AI芯片软件栈源码片段。现有分层标准如下 L0 硬件抽象层L1 驱动层L2 运行时层L3 编译算子层L4 框架适配层L5 工具层。 请阅读以下路径列表和核心文件片段完成 1. 把每个路径判到合适的层输出confidence分数0-1 2. 对每个路径写一句话职责描述 3. 标注关键对外接口名函数、结构体、服务名 输出严格JSON不要附加解释。执行Agent返回的结构大致是这样的{ layer: L2, module_name: dma_manager, path: runtime/src/dma/dma_manager.cpp, responsibility: 管理DMA传输的队列调度和内存描述符, confidence: 0.93, evidence: file_operations中存在ioctl入口依赖L1的buffer_alloc, exported_interfaces: [dma_copy, dma_wait, dma_pool_create] }毫秒级的关键在这里一定要让Agent给证据并打分不要只看它的结论。后续的校验Agent和人工抽查全靠这个证据字段来快速判断对错。3.3 第三步依赖关系建模分层做完之后地图的骨架已经有了接下来要填入“边”——也就是模块之间的依赖关系。这里的工程复杂度远高于分层。我拆成三类依赖分别抽取编译期依赖从CMakeLists.txt、Makefile、头文件include、link target中提取这是最机械但也最准确的一类。运行期依赖从调用链、驱动注册回调、runtime派发策略中提取Agent需要看代码逻辑才能判断。数据流依赖从上层框架到算子库的tensor传递路径、设备内存分配路径中提取这类依赖更像架构层面的依赖。Agent在处理这三类依赖时我建议分开跑不要让一个任务同时处理三类信息。先让“静态扫描Agent”去读构建脚本产出编译期依赖清单再让“源码理解Agent”去阅读关键调用路径产出运行期依赖最后由主Agent合并去重。合并时有一个容易忽视的问题循环依赖。实际软件栈里经常有A依赖B、B又依赖A的情况如果地图把这种边全部画出来图会非常丑陋。我的经验是在模块粒度上允许标注双向依赖但在分析结果里要标明“这是构建层面的循环还是逻辑层面的循环”前者通常是设计失误后者往往只是接口分层不纯粹。3.4 第四步地图生成与导航索引依赖模型建好后接下来就是渲染和落地。这一步我建议输出三种形态覆盖不同使用场景。第一种是Markdown结构化地图适合放进Git仓库、文档站是人肉眼快速扫全貌的首选。每一层是一个H2小节每个模块是表格的一行包含路径、职责、关键接口、依赖模块。第二种是知识图谱结构用图数据库或者JSON格式存节点和边。节点是模块边是依赖关系。后续的问答导航就是在这个结构上做检索。第三种是矢量化的检索索引把每个模块的职责描述、接口名、文档片段向量化存入向量库。Agent在回答“某个功能的代码入口在哪”时先在这层做一个语义检索召回再去做深度推理。三种形态互不替代。Markdown给人看知识图谱给系统读向量索引给Agent当记忆用。3.5 第五步校验修正闭环地图生成后必须过校验关。我实际用了三道校验缺一个都觉得不放心。第一道是构建级校验把地图中标出的编译依赖和真实CMake构建产物做比对。如果我告诉你这个模块链接了这个库但实际Makefile里根本没有这个target那这条依赖就要被标红。这个步骤可以用脚本自动跑。第二道是测试级校验抽取地图上的关键接口到源码里搜索这些接口的有向调用关系看是否与地图描述的路径一致。比如地图说“conv算子入口调用runtime算子调度器”那代码里从算子入口函数到调度器函数之间必须存在一条可达的调用链。Agent可以执行静态分析工具去验证这条链。第三道是人工抽查。不要怕抽查这是Agent工作流里不能少的一环。我的比例是前几轮至少抽查20%的模块重点抽查跨层依赖和置信度低的节点。后续稳定了可以降到5%。抽查不是走个形式发现错误要当场反馈给主Agent做修正并把纠错过程写进记忆。4. 实操案例跑通一张最小可用的芯片软件栈地图4.1 输入准备拿什么素材起步理论讲再多不如跑一次。我在这里用一个“类GPU架构的AI加速卡”来做演示不绑定具体硬件型号规避芯片厂商的特殊接口细节但流程完全通用。启动一个最小项目目录结构长这样chip_sw_map/ ├── inputs/ │ ├── trm/ # 芯片技术参考手册原始资料 │ ├── src/ │ │ ├── kernel_driver/ # 内核态驱动源码 │ │ ├── user_driver/ # 用户态驱动源码 │ │ ├── runtime/ # 运行时源码 │ │ ├── compiler/ # 编译器源码 │ │ ├── ops/ # 算子库源码 │ │ └── tools/ # 工具链源码 │ └── releases/ # Release notes和版本说明 ├── scripts/ │ ├── bucket_split.py # 资料分桶脚本 │ ├── static_scan.py # 静态扫描脚本 │ └── verify_build.py # 构建依赖校验脚本 └── output/ ├── sw_map.md # 地图Markdown ├── sw_map_graph.json # 依赖图JSON └── nav_index/ # 向量索引目录第一次跑不需要收集全部源码关键是跑通链路。我的建议是每个层挑2到3个代表模块总共十五六个模块就足够了。跑通之后再逐步扩到全量。4.2 Agent编排与工具组合这次我用的是一个多Agent的轻量编排方案一个主Agent加两个子Agent再加一个独立的校验Agent。我画一下配合关系你就明白为什么这样拆。主Agent负责任务拆解、进度监控、汇总结果和终稿输出。它不直接处理原始文件这是刻意为之。两个子Agent中一个叫“扫描Agent”只做一件事根据配置读取文件树和源码片段产出标准化的模块描述、层级判断和依赖证据另一个叫“文档Agent”负责阅读TRM和Release notes输出硬件特性、接口版本、历史变更日志的归纳。校验Agent单独拉出来专门处理主Agent汇总后的不一致比如“扫描Agent把dma_manager归到了L2但文档Agent在TRM里读到DMA初始化由L1驱动负责”这种冲突就必须由校验Agent裁决并返回修改建议。工具方面我给扫描Agent挂了三个工具文件系统读取、命令行执行用于跑grep、find、clang-query这类静态检索命令、Python代码执行用于跑我自己的分析脚本。文档Agent挂了两个工具PDF/文本解析器和向量检索器。校验Agent挂的是构建脚本分析和关键词检索工具。4.3 关键实现分层扫描脚本示例我先贴一段多层扫描的核心脚本。这段代码解决的是“从路径树到候选模块”的粗筛把机械工作交给规则把判断工作交给Agent。import os import json from pathlib import Path LAYER_RULES { L0: [register, mmu, sram, cache_ctrl, dma_raw], L1: [kernel_driver, kmd, umd, firmware, boot, irq], L2: [runtime, stream, context, memory_pool, sync], L3: [compiler, mlir, tvm, triton, kernel, ops_lib], L4: [pytorch, onnx, tensorflow, adaptor, bridge], L5: [profiler, debugger, converter, flash_tool, ci] } def apply_rules(path: str) - str: 基于路径关键词的粗筛只做第一轮候选不做最终决策 p path.lower() for layer, keywords in LAYER_RULES.items(): for kw in keywords: if kw in p: return layer return None def scan_tree(root: str) - dict: 扫描目录输出候选模块清单 candidates [] for dirpath, dirnames, filenames in os.walk(root): # 跳过构建产物和第三方依赖 dirnames[:] [d for d in dirnames if d not in (build, third_party, .git)] for f in filenames: full Path(dirpath) / f rel str(full.relative_to(root)) ext f.rsplit(., 1)[-1].lower() if ext not in (cpp, c, h, hpp, py, cmake, cc): continue candidate { path: rel, layer_guess: apply_rules(rel), size_bytes: full.stat().st_size, critical_keywords: [] } # 再抽取文件头部注释或文件名里的关键行为词 for kw in [dma, conv, gemm, scheduler, allocator, kernel]: if kw in rel: candidate[critical_keywords].append(kw) candidates.append(candidate) return candidates if __name__ __main__: root_path inputs/src output scan_tree(root_path) with open(output/candidates.json, w, encodingutf-8) as f: json.dump(output, f, ensure_asciiFalse, indent2) print(fscan done, {len(output)} files)这个脚本的输出不是最终地图而是给扫描Agent的“考试范围”。机械规则先过滤一次Agent再去精读每个候选文件的核心内容最终给出有依据的层级判断。跑出来的 candidates.json 就是子Agent的输入之一。接着扫描Agent对每个候选模块做深度描述。我给它设计的核心Prompt骨架是这样的以下是某个AI芯片软件栈的候选模块清单片段。 请对每个候选模块做判断 1. 当前模块的核心职责是什么用一句话说清楚。 2. 它属于哪一层只允许L0-L5 3. 它的关键依赖模块有哪些从它import、include、调用关系里识别 4. 它的对外接口有哪些 5. 你的置信度打分是多少如果低于0.6请说明不确定的点。 输出JSON列表。4.4 输出效果从Markdown地图到问答式导航跑完一轮我拿到一份十几MB源码对应的结构化地图。Markdown版本长这样## L2 运行时与资源管理层 | 模块 | 路径 | 职责 | 关键接口 | 依赖 | |---|---|---|---|---| | dma_manager | runtime/src/dma_driver/* | DMA队列调度与内存描述符管理 | dma_copy, dma_wait | L1 buffer_alloc, L3 ops_allocator | | memory_pool | runtime/src/mem/pool.cpp | 设备内存池管理与复用 | pool_create, pool_alloc | L1 kmd_bind, L2 sync_primitive | | stream_engine | runtime/src/stream/scheduler.cpp | 计算流调度和异步执行 | stream_submit, stream_sync | L2 memory_pool, L3 kernel_launch |这份Markdown文档是可以直接进团队Wiki的。但我更常用的是把focus放在问答式导航上。比如团队里有人问“算子库里的conv实现入口在哪它走运行时哪条路径才能到硬件”传统工作流是先翻算子库再找runtime的派发逻辑再对驱动接口三个人工跳转快的也要半小时。现在用Agent配合刚才的地图来做它的回答链路是先从向量索引召回conv相关模块描述再从graph JSON里找出依赖边然后顺着图从算子层走到驱动层最后在Markdown地图上定位文档章节。实测下来这一类问题从提问到得到带路径的答案大约在一分钟以内而且路径必经的证据点都会列出来。这套东西一旦配好团队里任何人问软件栈位置类问题都不用再去翻代码。5. 踩坑实录Agent画地图时的七个高频问题5.1 幻觉层级张冠李戴第一个坑几乎必踩Agent光看文件名很容易把驱动层里的内存管理模块误判到运行时层。我遇到过一个很典型的例子某文件叫gpu_mem.c它其实是内核态驱动里处理显存映射的模块Agent却把它放到了L2运行时理由是“有mem字样的都是内存池”。这就是典型的只看表面不看实现。解法强制要求Agent引用证据并给证据设置一个硬门槛。凡是证据字段为空、或者置信度低于0.7的节点必须进入人工复核名单。宁可让Agent说“不确定”也不能让它硬填。5.2 版本与分支混乱芯片SDK几乎总是同时维护多个版本分支。有的模块在v1.0版本里有某接口到v2.0已经改成另一套机制了。如果Agent不分版本地混合阅读地图上就会出现“同一个接口被两个模块以不同签名依赖”的诡异情况。我的做法是在采集阶段就按版本分支分桶。一次地图编制只针对一个主版本其他版本单独建分支维护。如果需要在同一张图上体现演进关系我会把“引入版本号”作为边的一个属性来标注而不是混在一起。5.3 依赖关系爆炸与环形依赖第一次构建依赖图边数比节点多了十倍不止图上密密麻麻全是线条视觉上已经废了。更麻烦的是环形依赖编译层说运行时反依赖它运行时又说编译层调了它的接口Agent在处理这种环的时候很容易死循环。我的处理策略第一把图粒度控制在模块级不要把函数级调用全部铺开函数级信息保留在模块描述文本里而不是画成图里的边第二对环做专门检测如果A和B是环地图里只保留一条主边另一条写成备注“循环依赖已折叠”。5.4 上下文窗口溢出把整个软件仓库塞进上下文是新手最容易犯的错误。一次我试着把编译器目录全量喂给Agent做分析结果还没跑到一半上下文窗口就爆了Agent开始胡言乱语输出质量急剧下降。解决方式有两个。一个是在目录颗粒度预先用无上下文成本的文件名规则做粗筛减少进入Agent的候选范围。另一个是把分析做成“流水线”先按层分桶再按模块切片每个模块单独分析再把结果汇总给主Agent。Agent不需要同时看完全部源码它只需要在汇总阶段面对所有模块的描述文本而这个描述量级已经很小了。5.5 输出格式不稳定同一个Agent跑两轮第一轮输出JSON第二轮开始在你要求的JSON前面加一大段开场白然后还嵌套了一层多余的数组。如果直接拿这个输出做下游解析必然报错。解法是强制结构化输出。现在的通用模型大多支持“response format”这类参数把它设置为json_object之后再结合prompt里的schema约束基本能保证格式稳定。如果还是偶尔抽风就加一个“解析失败重试一次”的兜底逻辑。5.6 保密资料混入芯片软件栈里有相当部分的代码和文档是有license或者访问范围限制的。如果Agent在采集阶段把所有文件都抓到一个开放目录里生成索引就会产生合规风险。我的建议是给输入目录增加白名单和分级机制。公开文档直接进地图内部受限SDK的模块只在地图上保留“模块名职责一句话内部Wiki链接”不要把敏感源码切片写入向量库。地图可以导航到它但详细内容必须在有权限的系统中打开。5.7 过度依赖Agent缺少人工校验最危险的问题不是Agent犯错而是团队对Agent的输出逐步丧失警惕。跑几轮之后地图看起来越来越像那么回事抽查比例就被悄悄降低了。直到有一次一个同事照着地图去追驱动初始化路径发现关键函数名和实际代码根本对不上才暴露了问题。我的经验是在Agent工作流里写死一个人工门禁。地图发布到Wiki之前必须由至少一名熟悉该层软件架构的工程师签字。越是看起来“完美”的地图越要留一双眼睛去挑毛病。6. 这张地图不只是“画着看”的6.1 从地图到导航问题定位速度快了一个量级地图做出来之后最直接的价值体现就是日常问题定位。我举一个刚遇到的事件模型推理时经常出现偶发性卡顿传统排查要同时怀疑内存池碎片、DMA队列冲突、算子调度瓶颈三个方向通常要一个下午。现在基于软件栈地图的排查方式完全不同先把“卡顿”现象输入AgentAgent根据地图检索出与推理路径相关的模块再结合profiler的输出圈定最可疑的两条调用链。短短十几分钟就发现是DMA manager在处理大块连续内存时的排队策略和内存池的碎片回收机制产生了交互阻塞。如果没有地图你根本不知道这两个模块之间存在这么隐蔽的运行时依赖。地图的价值在定位问题时不一定是“直接给出答案”而是把排查范围精确缩小一个数量级这个收益在大型软件栈里是非常可观的。6.2 从地图到自动编译链地图里的依赖关系补齐之后其实已经变相拿到了“构建时的模块依赖清单”。更进一步可以用它来自动生成某个子模块的编译配置模板。比如你只想单独编译算子库里的算子但不知道它依赖哪些运行时头文件和驱动接口地图上就有现成的答案。我试验过把地图JSON对接给后端系统让它自动生成一个裁剪过的CMakeLists专门用来做算子单元测试。跑通之后感觉前途很大但门槛也不小前提是地图里的依赖关系必须保持高准确率否则生成的配置会漏掉隐性依赖。所以这个方向建议在核心层模块上先试点不要一上来就全铺开。6.3 沉淀成团队知识库地图最大的后劲在于它是一个可以持续更新的知识底座。我的做法是在CI里加一个定时任务每次代码大更新或者Release发布后触发一次增量地图更新新模块添加、旧模块改路径、接口变更都会在diff里高亮。更新完自动生成一个“地图变更报告”发到团队群聊里。这样做的好处是地图不是一次性项目而是变成了团队知识体系里活的一部分。新人来了不用问“这个模块是干嘛的”地图上写得清清楚楚老员工离职也不用担心“某个模块的信息就断档了”因为地图已经把这些信息以结构化的方式留在了团队里。我自己实际操作下来的体会是地图本身画得再精美如果不进入日常研发流程迟早会过时过时就会失去信任。真正让这套方案跑出价值的不是“一次性画完图”而是把“定期更新地图”纳入团队的习惯。Agent在其中解决的最大问题不是替代人的判断而是把重复、琐碎、跨文档的信息整理工作接管过去让工程师把精力花在真正需要经验的地方。所以如果你也打算做类似的事情建议从小到大起步先拿十几个模块跑通流程再逐步扩展到全量。等地图开始回答第一个实际问题的那个瞬间你就知道这件事做对了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑