高德运营同学半年内从零搭建自动化日报系统,用AI解放人力,实现数据到阅读仅需10分钟。
核心内容:
- 运营日报的痛点与AI化契机
- 从静态站点开始的CI初体验与踩坑历程
- 自动化流水线的完整搭建与实战效果

阿里妹导读
文章内容基于作者个人技术实践与独立思考,旨在分享经验,仅代表个人观点。
摘要
作为高德打车区域运营的一员,和所有运营同学一样,日常工作中最耗精力的就是盯数据、写日报、做分析。半年前,对“CI/CD”这个词一无所知,甚至觉得代码库、git这些东西都是开发同学才需要碰的。
但半年后,一套完整的日报自动化流水线已经独立上线:每天定时从ODPS取数,用Qwen大模型自动生成解读,钉钉群自动推送,在线站点自动部署。整个过程无需人工干预,从数据到阅读只需10分钟。
这篇文章不是技术教程,而是一线运营的真实实战记录——从零开始,一步步踩坑、一步步借力AI解决技术问题,最终把整套系统跑起来的故事。
一、背景:为什么运营要搞CI
1.1 日报的痛点
运营团队应该都需要有这样一份日报,要覆盖前一天的目标达成、大盘数据、商户异动、城市峰值、分时应答情况等诸多模块。以前,这份日报的制作流程是这样的:
- 打开ODPS进行SQL取数
- 把数据复制到Excel里整理
- 手动写分析解读
- 排版成文档,发到钉钉群
- 如果要留档,还得手动上传到某个地方
每天至少花1小时,而且依赖人工读数和业务经验,分析质量高度取决于处理人的经验和细心程度。
1.2 团队AI化的契机
2026年初,团队开始全面推进AI工具落地。Qwen Code、提示词工程、自动化流程……大家都在探索怎么用AI把重复性工作自动化。
团队leader提出了一个想法:日报能不能自动化?每天的数据结构是固定的,分析框架是固定的,唯一变化的是数字本身——这不就是最适合自动化的场景吗?
1.3 为什么选Aone CI
说实话,一开始根本不知道用什么工具。尝试过几个方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 本地Python脚本 | 简单直接 | 每天要手动跑,电脑关了就无法运行 |
| 定时任务(crontab) | 能定时 | 依赖某台机器,不透明 |
| Aone CI流水线 | 云端运行、有日志、能定时触发 | YAML语法,零基础有学习成本 |
最终选Aone CI的原因很简单:代码仓库就在Aone上,CI是现成的能力,不用额外搭环境。而且团队里已经有同学在用CI做静态站点托管,有现成的经验可以参考。
二、第一次接触CI:从静态站点开始
2.1 第一个任务:让HTML能被在线访问
CI之旅不是从日报开始的,而是从一个更简单的需求开始——把HTML文件放到在线站点上。
在Aone代码库里有一个静态站点(Aone Pages),可以把HTML文件托管成在线页面。团队里其他同学已经搭好了这个站点,需要做的是:把生成的HTML文件推送到仓库的public/目录下,它就能自动部署到在线地址。
听起来很简单对吧?但实际操作起来,坑一个接一个。
2.2 第一个坑:实战中理解代码库的分支隔离
在master分支的public/文件夹里放了一个HTML文件,然后发现——自己的分支能看到这个文件夹,但其他同学的分支看不到。
当时很困惑:明明在master里建了文件夹,为什么别人的分支没有?
后来问了AI才知道,这是Git分支隔离的正常现象。每个分支是独立的,public/文件夹只存在于创建或合并了它的那些分支上。要让别人的分支也有,需要每个人自己执行一次git pull origin master。
2.3 第二个坑:master是保护分支
更头疼的问题来了。仓库的master分支是受保护分支,不能直接push。每次往public/目录合入新的HTML文件,都需要提MR(Merge Request)审核。
这意味着:
- 每次上传HTML → 提MR → 等审核 → 合入master → 手动更新README(生成新的访问链接)
- 流程繁琐,效率很低
2.4 用CI解决:自动更新README
如果能配置一条流水线,在每次push HTML文件后自动生成README索引并推送回去,不就不用手动更新了吗?
这是第一次真正动手配置CI流水线。整个过程是在AI的辅助下一步步完成的,大致分为以下几步:
第一步:申请PAT(个人访问令牌)
CI容器里要往仓库push代码,需要一个“通行证”——PAT(Personal Access Token)。CI自带的Token是只读的,不能push。
操作路径:打开 code.alibaba-inc.com → 右上角头像 → 设置 → Private Token → 重置并复制。
第二步:配置Secrets
PAT不能明文写在YAML里(会泄露到日志中),需要通过仓库的变量管理配置为Secrets类型。
| 类型 | 引用语法 | 适用场景 |
|---|---|---|
| Secrets | ${{ secrets.X }} | 敏感信息(Token、密码、API Key) |
| Variables | ${{vars.X}} | 非敏感配置(项目名、端点地址) |
千万别混用——用secrets语法引用Variables类型的变量,值会是空字符串。
第三步:写YAML流水线
这是写的第一条YAML流水线(.aoneci/update-readme.yaml),核心逻辑是:
- 检测本次push是否涉及
public/*.html文件变更 - 如果有,执行scripts/update-readme.sh脚本重新生成README
- 用PAT把更新后的README推回当前分支
写YAML的过程充满了挫折感——语法错误不会报错,只是流水线不触发。比如trigger要写成triggers(复数),run: |要写成数组格式,checkout要写成- uses: checkout而不是- checkout。这些调试过程全部依赖AI读取运行日志,一步一步和AI共创问题的解决方案。在这个过程中,不禁想起网上那个梗——“和AI对话的时候,就像绝望的李鸿章,不管他端上来什么,只能无能地点【接受并运行】”......
由此总结出一个经验:不要从零写YAML,先复制仓库里已经跑通的流水线,再改。
2.5 跑通的那一刻
当第一次看到流水线执行成功、README被自动推回分支的时候,那种成就感是真实的。
[INFO] 当前分支: qioyue_370902
[INFO] Token 长度: 20
[INFO] 检测到 HTML 变更: public/test_ci.html
[INFO] 推送到 origin/qioyue_370902(用户: judy.qy)
[OK] README 已自动推回 qioyue_370902虽然只是一个简单的README自动更新,但它让人理解了CI的基本工作原理:触发条件 → 执行步骤 → 产出结果。这为后面搭建日报流水线打下了基础。
三、搭建日报自动化流水线
3.1 整体架构设计
有了CI的基础认知后,开始设计日报自动化流水线。整体思路是:
定时触发 → 取数 → 生成日报 → 转HTML → 推钉钉 → 推仓库 → 自动部署具体来说,分为7个步骤:
| 步骤 | 动作 | 工具/技术 |
|---|---|---|
| 1 | FBI数据抓取 | DataClaw API |
| 2 | ODPS SQL执行 | MaxCompute(5个模块各配专属SQL) |
| 3 | AI生成日报 | Qwen大模型(自然语言总结) |
| 4 | Markdown转HTML | Python脚本 |
| 5 | 推送钉钉 | Webhook + 加签 |
| 6 | HTML留档 | git push到个人分支的public/目录 |
| 7 | 自动部署 | 触发deploy-pages流水线 |
3.2 变量配置:一次配好,全仓库共享
日报流水线需要很多配置:ODPS的AccessKey、Qwen的API Key、钉钉Webhook地址等等。
好消息是:代码库的变量配置是仓库级别共享的。只要在Aone CI的变量管理中配置一次,同仓库的所有YAML流水线都可以通过相同语法调用。
配置的变量包括:
| 变量名 | 类型 | 用途 |
|---|---|---|
| ODPS_ACCESS_ID | Secret | MaxCompute AccessKey ID |
| ODPS_ACCESS_KEY | Secret | MaxCompute AccessKey Secret |
| DASHSCOPE_API_KEY | Secret | Qwen大模型API Key |
| DINGTALK_WEBHOOK | Secret | 钉钉机器人Webhook URL |
| GIT_PUSH_TOKEN | Secret | PAT(用于HTML推送回仓库) |
| ODPS_PROJECT | Variable | ODPS项目名 |
这里有一个值得分享的经验:ODPS权限建议用团队公共账号。申请一个公共AccessKey,授权所需的数据表,然后统一配置到仓库Secrets中。这样:
- 避免个人AK泄露风险
- 人员变动无需更换凭证
- 新人上手零配置
3.3 两条流水线的分离设计
最终搭建了两条独立的流水线:
流水线一:daily-report-auto(日报生成)
- 镜像:
python:3.11 - 触发:工作日10:30定时 + 个人分支push
- 职责:取数 → 生成日报 → 推钉钉 → 推HTML到public/
流水线二:deploy-pages(站点部署)
- 镜像:
alios-8u(自带jq + git) - 触发:push到public/时实时触发 + 每天17:00定时兜底
- 职责:合并所有分支的public/内容 → 部署到生产站点
为什么要拆成两条?因为Aone CI的uses:组件运行在隔离容器中,python:3.11镜像缺少deploy-pages需要的jq工具,而且无法在流水线中补装。所以把“生成”和“发布”拆开,各用各的镜像,互不干扰。
四、并发问题:多人协作的终极挑战
日报流水线跑通后,以为大功告成了。但很快遇到了一个更棘手的问题——多人协作时HTML互相覆盖。
4.1 问题是怎么发生的
团队只有一个Aone Pages静态站点,deploy-pages每次部署是全量替换(不是增量追加)。
场景还原:
- 在个人分支生成了日报HTML → 推到
public/daily-report/→ 触发部署 → 站点上有了日报 ✅ - 其他同学往master分支合入了一个活动页面 → 触发master的deploy-pages → 用master的public/全量替换站点
- 日报消失了 ❌(因为master的public/里没有daily-report/)
核心矛盾:所有人都在各自分支生成HTML,但站点只有一份,每次部署都是全量替换。
4.2 解决方案:每次部署前合并所有分支
思路很明确:每次部署前,先把所有远程分支的public/**内容合并到一起,再部署完整集合。
具体实现是在deploy-pages.yaml的部署步骤前加一个“合并”环节:
# 动态发现所有远程分支
git fetch origin --depth=1
ALL_BRANCHES=$(git branch -r | grep -v 'HEAD' | sed 's|origin/||' | tr -d ' ')
# 逐一拉取每个分支的public/内容
for branch in $ALL_BRANCHES; do
git fetch origin $branch --depth=1 2>/dev/null || true
git checkout origin/$branch -- public/ 2>/dev/null || true
done
# 当前触发分支的内容最后覆盖(保证最新变更优先)
git checkout HEAD -- public/ 2>/dev/null || true这样无论谁触发部署,所有人的HTML都会先合并进来,再一起部署。
4.3 为什么每个分支都要有deploy-pages.yaml
Aone CI读取的是被推送分支上的YAML。如果分支上没有deploy-pages.yaml,推public/时不会触发任何部署。
好消息是:所有分支的YAML内容完全一样,不用各自定制。如果你是从master新建的分支,这个文件已经自动继承了。
4.4 防护机制
设计了几层防护,确保系统健壮:
| 异常场景 | 防护措施 |
|---|---|
| 新分支没放yaml | 不会触发部署,不会覆盖别人(无害) |
| 新分支放了正确yaml | 自动合并所有分支内容后部署(安全) |
| 新分支放了错误yaml | 下次正确分支部署时自动恢复 |
| 站点被意外覆盖 | 每天17:00定时部署自动恢复完整内容 |
17:00的定时任务是一个兜底机制,应对极端情况。正常情况下,每次push触发的实时部署就已经包含了所有分支的内容。
4.5 为什么不用[skip ci]防循环
最初想用[skip ci]标记来防止部署流水线重复触发,但后来发现[skip ci]会杀死所有流水线(包括日报生成流水线)。
更好的方案是用paths过滤:只在public/**有变更时才触发deploy-pages。这样不该跑的流水线不会被误杀。
五、经验总结与可复用模板
5.1 新人接入最容易踩的坑(实战总结)
以下是搭建CI流水线过程中真实踩过的坑,涵盖语法陷阱、环境隔离和平台机制差异,按配置流程顺序排列:
| # | 坑点 | 现象 | 根因 | 正确做法 |
|---|---|---|---|---|
| 1 | YAML触发器关键字写错 | push后流水线列表无新任务 | 必须写 triggers:(复数),写成 trigger: 不报错但不生效 | triggers: 固定复数,下面嵌套 push: / schedule: |
| 2 | Secrets/Variables语法混用 | 环境变量为空,脚本报"key not found" | 两种类型引用语法不同,混用取空 | Secrets用 ${{ secrets.X }},Variables用 ${{ vars.X }};Variables需在YAML中显式export |
| 3 | git push报403 | fatal: The requested URL returned error: 403 | 阿里内网不支持 oauth2:TOKEN 格式 | 必须用 https://用户名:PAT@code.alibaba-inc.com/repo.git 格式 |
| 4 | uses:组件看不到前面装的工具 | apt install jq 成功但 uses: deploy-pages 报 "jq: command not found" | uses: 运行在完全隔离的容器,不继承run步骤的环境 | 拆成两条流水线,各用各的镜像 |
| 5 | $CI_COMMIT_BRANCH不存在 | fatal: invalid refspec 'HEAD:' | Aone CI没有此内置变量(不是GitLab CI) | 用 git rev-parse --abbrev-ref HEAD 动态获取分支名 |
| 6 | 定时任务写在个人分支不触发 | cron配了但到点无反应 | schedule调度器只从master分支读取配置 | YAML必须push到master;job内 git fetch + checkout 切个人分支执行 |
| 7 | cron时间差8小时 | 想10:30触发,凌晨2:30跑了 | Aone CI cron按北京时间CST解释,不是UTC | 直接写北京时间,如 "30 10 * * 1-5" |
| 8 | [skip ci]杀全局 | 循环解决了但日报流水线也不跑了 | [skip ci] 全局生效,所有流水线都不触发 | 用 paths 过滤精确控制触发范围,如 daily-report/** / public/** |
| 9 | 手动重跑用旧配置 | 改了YAML但重跑还是旧逻辑 | 重跑加载的是所选commit对应的YAML | 改完YAML先push新commit(不含[skip ci]),再触发新commit |
| 10 | run:多行语法报错 | YAML解析通过但执行报语法错误 | Aone CI的 run: 要求数组格式 | 写成 run: 下接 - | 数组元素,不能用 run: | |
总结心法:遇到CI问题别自己死磕。把完整的报错日志复制给AI,让它帮你分析定位。配置CI的效率不取决于记住了多少语法,而是能不能快速定位问题。
5.2 调试CI的正确姿势
善用调试三板斧:查官方手册 → 对比已跑通的YAML → 加诊断输出。
遇到报错不要慌,把完整的日志信息复制给AI,让它帮你分析。这是整个CI搭建过程中最高效的调试方式。
5.3 关键设计决策回顾
| 决策 | 为什么这么做 |
|---|---|
| 全部部署在个人分支 | master有保护,无法直接push;个人分支无限制,PAT即可跑全流程 |
| 用独立流水线做部署 | 日报流水线的环境缺少发布所需的工具且无法补装,拆开后互不干扰 |
| 用paths而不是[skip ci]防循环 | [skip ci]会杀死所有流水线;paths只拦不该跑的 |
| 用团队公共ODPS账号 | 避免个人AK泄露风险,人员变动无需换凭证,新人零配置 |
| SQL取数+AI总结 | 数据准确性由SQL保证(0幻觉),AI只负责自然语言解读 |
5.4 关键代码文件清单
如果你想搭建类似的日报自动化流水线,以下是需要关注的文件:
| 文件 | 职责 |
|---|---|
.aoneci/daily-report-auto.yaml | 主流水线:触发器 + 环境变量注入 + 日报生成全流程 |
.aoneci/deploy-pages.yaml | 部署流水线:push public/实时触发 + 定时兜底 |
daily-report/config.py | 统一配置:路径、URL、开关、仓库信息 |
daily-report/generator.py | ODPS SQL执行 + Qwen prompt组装 + Markdown生成 |
daily-report/md_to_html.py | Markdown → 自包含HTML转换 |
daily-report/notifier.py | 钉钉Webhook推送(加签 + 精简格式) |
daily-report/publish_html.py | HTML推送到public/ + 触发Pages部署 |
daily-report/sql/ | 各模块专属SQL文件 |
daily-report/prompts/ | 各模块Qwen prompt模板 |
写在最后
回头看这半年的经历,从一个连YAML是什么都不知道的运营,到能独立搭建和维护CI自动化流水线,最大的感触是:
AI不会替代运营,但会用AI的运营会替代不会用的。
搭建CI的过程中,遇到的每一个技术问题——YAML语法、Git认证、ODPS权限、多分支并发——都是在AI的辅助下解决的。不需要记住每一个技术细节,只需要知道要实现什么效果,然后把报错信息告诉AI,让它帮忙找到解决方案。
当然,有些东西是需要自己理解的:
- 业务逻辑:哪些数据重要、怎么分析、怎么呈现——这是运营的核心能力
- 系统设计:为什么要拆两条流水线、为什么要合并分支——这些决策需要理解业务场景
- 质量把控:AI生成的内容需要人工校验,数据准确性需要机制保障
技术工具在变,但运营的核心价值没变:用数据驱动决策,用效率创造价值。CI只是帮你把重复性工作自动化了,让你有更多时间去做真正需要人判断的事情。
如果你也是一个想用技术提效的运营同学,建议是:不要怕,从最简单的开始,一步步来,遇到问题就问AI,与AI协同找到解决方案。你会发现,那些看起来很“技术”的东西,其实并没有那么难。
本文是「火种&起源」系列的一部分,记录一线运营同学在AI工具实践中的真实故事。
