AI Agent 套件。基于 pi-mono 构建, 包含两个产品:GitLab Code Review Agent 与 钉钉运维机器人。
本项目实现两个不同形态的 AI Agent:
- 由 CI/CD 触发,每次 MR / commit 自动跑
- 拉取项目代码,分析本次提交的变更
- 按可配置的编码规范(skill 文件)给出评审意见
- 把意见以行内 / 整体评论的形式发回 MR
- 用户在钉钉群 @机器人发消息
- 实时调用阿里云 ARMS / SLS 查日志、看监控、查告警
- 多轮对话,记住上下文
- 流式回复给用户
两个产品共享 70% 的底层能力(LLM provider、工具、合规、观测/审计), 但生命周期、入口、会话模型完全不同,所以独立部署、独立进程。
| 维度 | code-reviewer | ops-bot |
|---|---|---|
| 生命周期 | 一次性(几分钟) | 长驻服务(7×24) |
| 触发方式 | CI pipeline 事件 | webhook(钉钉消息) |
| 会话模型 | 无状态 | 有状态(按会话隔离) |
| 并发 | 单实例 | 多用户并发 |
| 输出渠道 | GitLab MR 评论 | 钉钉流式回复 |
| 环境 | 容器内、临时 | 长驻服务器 / K8s |
┌────────────────────────────────────────────────────────────┐
│ Flower (本仓库) │
│ ┌──────────────────────────┐ ┌────────────────────────┐ │
│ │ code-reviewer │ │ ops-bot │ │
│ │ 套 pi-coding-agent │ │ 直接用 pi-agent-core │ │
│ └────────────┬─────────────┘ └────────────┬───────────┘ │
│ │ │ │
│ └──────────┬───────────────────┘ │
│ ▼ │
│ 共享层(provider、tools、compliance) │
└──────────────────────────┬─────────────────────────────────┘
│ 依赖 npm 包
▼
@earendil-works/pi-coding-agent (上游)
@earendil-works/pi-agent-core (上游)
@earendil-works/pi-ai (上游)
- LLM 接入通过
pi.registerProvider()完成,不改 pi 源码 - 工具通过
pi.registerTool()添加 - 行为通过事件订阅(
pi.on())修改 - 必要时用 npm
overrides做"热插拔",fork 单个包重定向
GitLab CI Pipeline
│ (.gitlab-ci.yml 中 review job)
▼
docker run yourcompany/code-reviewer:latest --mr-iid $CI_MERGE_REQUEST_IID
│
▼
[code-reviewer 容器内]
1. clone 项目 / 切到目标 commit
2. 启动 pi -p "..."(print 模式)
3. pi 自带工具读代码(read / grep / find)
4. 调用 gitlab_post_comment 工具发评论
5. 进程退出,job 完成
│
▼
GitLab MR 上出现评论
钉钉群(@机器人)
│ webhook
▼
[ops-bot HTTP server]
receive { conversationId, userId, text }
│
▼
SessionStore.getOrCreate(conversationId) ← Redis
│
▼
Agent.prompt(text) ← pi-agent-core Agent 实例
│
│ stream events
▼
通过钉钉流式回复 API 推送回去
▲
│ 工具调用
│
├──► arms_query_logs
├──► arms_query_metrics
├──► arms_list_alerts
└──► arms_get_trace
它已经具备:
read/grep/find/bash工具——LLM 想看哪个文件直接看print模式(pi -p "...")——天生匹配 CI/CD 的 run-and-exit 形态- skill 系统(
/skill:code-review-checklist)——评审清单可以做成可复用资产 - 完整的合规拦截(可以禁用 write/edit,限制 bash 白名单)
CI 里的 agent 不能改代码。在 flower-compliance 里注册全局拦截器:
pi.on("tool_call", async (event) => {
if (event.toolName === "write" || event.toolName === "edit") {
return { block: true, reason: "CI mode: read-only" };
}
if (event.toolName === "bash") {
const cmd = event.input.command;
if (!/^(git|grep|find|ls|cat|head|tail|wc|file)\b/.test(cmd.trim())) {
return { block: true, reason: `CI mode: ${cmd.split(' ')[0]} not allowed` };
}
}
});packages/flower-code-reviewer/skills/
├── general.md # 通用清单
├── backend.md # 后端(并发、数据库、安全)
├── frontend.md # 前端(性能、可访问性)
└── security.md # 安全专项
入口脚本根据变更文件类型自动选 skill。
gitlab_get_mr_diff— 拿本次 MR 的 diffgitlab_get_mr_files— 拿被修改的文件列表gitlab_post_comment— 发整体评论gitlab_post_line_comment— 发行内评论(支持 severity)gitlab_get_previous_review— 避免重复评论
不要让 LLM 在 stdout 写"评审报告"——CI 日志没人看。
Prompt 强制要求:所有意见必须通过 gitlab_post_comment 工具发表。
最后扫描所有评论,如果有 severity === "blocker" 的,exit code 设 1,让 pipeline fail。
其他情况 exit code 0,只是警告。
pi-coding-agent 假设:
- 一个用户、一个进程、一个 TTY
- 会话存 jsonl 文件
- 默认工具是 bash / read / edit
钉钉 bot 需要:
- 一个进程服务多用户、多群
- 会话存 Redis,支持横向扩展
- 工具只有 ARMS 查询,没有文件操作
所以直接用 pi-agent-core 的 Agent 类自己做 HTTP server + 会话管理。
每个钉钉 conversationId 对应一个独立的 Agent 实例,messages 持久化到 Redis(24h 过期)。
钉钉同步 webhook 有 5 秒超时,运维查询常常 > 5 秒。 必须用钉钉的"流式 stream API"(企业内部应用 sessionWebhook 机制)。
const unsubscribe = agent.subscribe(async (event) => {
if (event.type === "message_update") {
accumulator += extractDelta(event);
await pushToSession(sessionWebhook, accumulator, false); // 500ms 节流
} else if (event.type === "agent_end") {
await pushToSession(sessionWebhook, accumulator, true); // final
}
});ARMS / SLS(来自 flower-tools-arms):
arms_query_logs— SLS 日志查询(支持 SLS 查询语句)arms_query_metrics— APM 指标(QPS / RT / 错误率 / 慢调用)arms_list_alerts— 列出活跃告警arms_get_trace— 查 traceId 的调用链
通用知识检索(来自 flower-tools-common):
zentao_search— 禅道任务 / 需求 / bug 检索dingtalk_doc_search— 钉钉知识库 / 文档检索
钉钉 bot 面向全员,必须按 DingTalk userId 鉴权:
pi.on("tool_call", async (event, ctx) => {
const user = await getUser(event.context.conversationId);
if (event.toolName.startsWith("arms_") && !user.canQueryArms) {
return { block: true, reason: "您没有 ARMS 查询权限" };
}
});ARMS 日志里可能有 PII(手机、身份证、邮箱)。在工具结果里就脱敏, 不要让 LLM 看到原始敏感数据(它可能复述到回复里)。
自定义 LLM 入口。两个产品都通过这里把 LLM provider(自部署网关 / 企业 AI Gateway / 任意 OpenAI 兼容服务)接入。
pi.registerProvider("havefun-anthropic", {
baseUrl: "https://ai-gateway.corp.internal/v1",
apiKey: "LLM_API_KEY",
api: "anthropic-messages",
models: [/* 允许使用的模型清单(各家族走原生协议)*/],
headers: { "X-App-Source": process.env.APP_NAME },
});跨产品通用的工具,面向使用禅道 / 钉钉的团队:
- 禅道(
zentao.ts)— 项目管理工具(查任务、需求、bug 等) - 钉钉文档(
dingtalk-doc.ts)— 钉钉知识库 / 文档检索
阿里云 ARMS / SLS 工具集。仅 ops-bot 使用(code-reviewer 不该看监控)。
GitLab API 工具集。仅 code-reviewer 使用。
注:ops-bot 不应该有写 GitLab 的能力,职责隔离。
合规拦截扩展(纯策略包):只做"判定 + 拦截",拦截事件经 onBlock 回调交产品层接到 telemetry。
- code-reviewer:
mode: "ci-readonly" - ops-bot:
mode: "production-readonly"(当前 no-op,工具本身只读)
观测事件管道:pi 事件归一化 + 可插拔 sink(JSONL / console / SIEM)。
- JSONL trace(CI artifact)= 评审质量迭代的数据基座(回放评估、评论采纳率)
- consoleSink 承接原 observability 的 CI 日志打印(
FLOWER_VERBOSE语义不变) - siemSink 承接原 compliance 的 SIEM 审计(metadata-only、critical 不可关、payload 兼容)
flower/
├── README.md # 你正在看的文件
├── package.json # npm workspaces 根
├── tsconfig.base.json # 共用 TS 配置
├── tsconfig.json # project references 入口
├── biome.json # 代码风格
├── .env.example # 环境变量模板
│
└── packages/
├── flower-providers/ # 共享:LLM provider 注册
├── flower-tools-common/ # 共享:通用工具
├── flower-tools-arms/ # 共享:ARMS 工具(给 ops-bot)
├── flower-tools-gitlab/ # 共享:GitLab 工具(给 code-reviewer)
├── flower-compliance/ # 共享:合规拦截(纯策略包)
├── flower-telemetry/ # 共享:观测管道(归一化 + JSONL/console/SIEM sink)
│
├── flower-code-reviewer/ # 产品 1
│ ├── src/
│ │ ├── cli.ts # CLI 入口
│ │ ├── args.ts # 命令行参数解析
│ │ ├── run.ts # 主流程(初始化 pi、跑评审、退出码)
│ │ ├── extension.ts # pi 扩展(注册工具/合规)
│ │ ├── skill-selector.ts # 按变更文件类型挑 skill
│ │ └── prompts.ts # 评审 prompt
│ ├── skills/ # 评审清单(skill 文件)
│ └── Dockerfile # 容器构建
│
└── flower-ops-bot/ # 产品 2
├── src/
│ ├── server.ts # HTTP 入口
│ ├── handler.ts # webhook 请求处理与回复编排
│ ├── dingtalk/ # 钉钉 webhook / 签名 / 推送
│ ├── agent-factory.ts # Agent 实例构造
│ ├── session-store.ts # Redis 会话存储
│ ├── tools.ts # ARMS 等工具组装与权限拦截
│ └── prompts.ts # 系统 prompt
└── Dockerfile
- Node.js ≥ 22
- npm ≥ 10
- (开发 ops-bot) 本地 Redis
# 1. 安装依赖
npm install
# 2. 准备环境变量
cp .env.example .env
# 然后编辑 .env 填上 LLM token、GitLab token 等
# 3. 类型检查
npm run typecheck
# 4. 构建所有包
npm run build
# 5. 代码风格检查
npm run check# code-reviewer 本地试跑
cd packages/flower-code-reviewer
npm run dev -- --mr-iid 123
# ops-bot 本地起服务
cd packages/flower-ops-bot
npm run dev
# 访问 http://localhost:3000/dingtalk/webhook| 问题 | 当前思路 |
|---|---|
| 谁付钱? | LLM 调用花钱。code-reviewer 每个 MR 一次,可控;ops-bot 全员用,必须做配额 |
| 谁负责? | bot 给出错误的运维建议导致事故,谁担责?关键操作必须"建议但不执行",所有 ARMS 工具都是读 |
| 数据合规? | 日志 / 代码可能涉密。LLM 调用建议走自部署 / 私有网关,而不是公网 LLM API |
| 可观测? | 两个产品自己也要被监控:每天处理多少 MR / 回答多少问题 / 错误率 / 延迟 |
| 回退方案? | LLM 挂了 / 超时 / 限流时:code-reviewer pipeline 继续(警告但不 fail);ops-bot 友好降级("AI 不可用,请直接查 ARMS 控制台") |
- 搭 monorepo 骨架
-
flower-providers接通目标 LLM 网关 -
flower-telemetry观测管道(JSONL trace 数据基座 + SIEM 审计收编,拦截事件不再漏报)
- GitLab 工具集(get_diff / post_comment)
- 1 个 skill(general-review)
- 测试项目 CI 接入,手动触发跑通
- MR 自动触发
- 添加 backend / frontend / security skill
- 评审质量回归(挑 20 个历史 MR 对比)
- 钉钉 webhook 接入(测试群)
- ARMS 日志 / 指标工具
- Redis 会话存储
- 流式回复
- 鉴权边界
- 数据脱敏
- 配额控制
- 灰度到一个业务团队
这些能力当前版本暂不实现,作为后续演进方向。详细方案见 packages/flower-ops-bot/README.md。
- Plan-then-Execute:复杂任务先列计划再执行(改 prompt 即可,P0)
- Diagnostic Sub-Agent:复杂诊断派给专项子 agent,小模型搞不定时升级到大模型
- 告警驱动主动触发:ARMS 告警自动启动诊断,主动推送结论
- 定时巡检(cron):每天自动巡检关键应用
- 用户订阅 Watcher:"盯住某指标超阈值就通知"
- 持久化执行计划:长任务中断可恢复
说明:当前 ops-bot 已经具备"自规划"能力 —— 一次用户消息内,LLM 可以 自主调用任意多个工具,基于结果迭代决策直到给出最终答案。这是 pi-agent-core agentLoop 的内建行为。上述进阶能力是在这个底座之上的扩展。
MIT © silentflower