乐于分享
好东西不私藏

AI审批助手:让流程在你打开之前就已思考完毕

AI审批助手:让流程在你打开之前就已思考完毕

审批场景和聊天场景有本质区别——审批是流程驱动的,事件由系统产生,处理人由流程指定。现有的 AI 工具都是”人主动问、AI 被动答”,但审批需要的是反过来的:流程产生事件后,AI 主动分析,人在打开页面时直接看到结果

这篇文章介绍一个完整的审批 AI 自动分析方案:后台轮询预分析 + 浏览器扩展秒开展示。所有代码和配置均已开源,你可以直接拿去适配自己的审批平台。

· · ·

一、产品哲学:服务流程,不服务人

市面上大多数 AI 助手是”服务人”的——你问它,它回答。你贴一段文字,它帮你分析。所有交互起点是你主动触发。

但审批场景不一样。审批是流程驱动的:事件发生在流程中,处理人也由流程指定。审批单不是”我想看就看”的内容,而是”到你手里你必须看”的任务。

所以核心设计理念是反过来:

不是「人打开页面 → 点击 AI 按钮 → 等分析结果」。而是「流程产生审批单 → AI 自动分析 → 缓存结果 → 人打开页面秒出」。

人进来看到的,不是一个”请点击分析”的按钮,而是一个已经写好的分析报告,上面还标着”3 分钟前已分析完毕”。

· · ·

二、系统架构全景

先看整体架构。系统由四个运行时组件构成,各司其职:

┌─────────────────────────────────────────────────────┐│                     审批平台 API                      ││            (你公司的 OA / BPM / 审批系统)              │└────────────┬────────────────────────────┬────────────┘             │                            │       ① 轮询拉取                     ② 页面访问     (每30秒一次)                   (用户打开审批单)             │                            │             ▼                            ▼   ┌─────────────────┐          ┌─────────────────────┐   │  watcher.py     │          │  Chrome Extension   │   │  后台常驻进程    │          │  content_script.js  │   │                 │          │                     │   │  发现新审批单    │          │  提取页面上下文      │   │  提取详情数据    │          │  校验处理人身份      │   │  匹配Skill规则  │          │  查缓存 / 实时触发  │   └────────┬────────┘          └──────────┬──────────┘            │                              │       ③ 调用分析                     ④ 展示结果            │                              │            ▼                              ▼   ┌─────────────────┐          ┌─────────────────────┐   │  bridge.py      │          │  panel.html/js      │   │  HTTP :39877    │          │  侧边栏面板          │   │                 │          │                     │   │  转发到QoderCLI │          │  SSE流式/缓存秒开   │   │  SSE流式回传    │          │  Typewriter效果     │   └────────┬────────┘          └─────────────────────┘            │       ⑤ AI推理            │            ▼   ┌─────────────────┐   │  qodercli       │   │  本地AI引擎     │   │                 │   │  Skill路由:     │   │  报价审批分析   │   │  方案评估       │   │  财报解读 ...   │   └────────┬────────┘            │       ⑥ 分析结果            │            ▼   ┌─────────────────┐   │  SQLite 缓存    │   │  HTTP :39878    │   │                 │   │  /result/{id}   │   │  /stats         │   └─────────────────┘

▲ 六步数据流:从审批平台到用户面板,全链路自动完成

· · ·

三、四大先进设计

这套系统不是简单的”API + Chrome 插件”,它有四个设计点值得拿出来说。

先进一:缓存先行(Cache-First),不是调用先行

传统的 AI 集成思路是”用户点击→调 API→等结果”。这条路在审批场景走不通——审批动辄十几页附件,AI 分析可能需要 30~60 秒。让审批人等一分钟?不可能。

所以设计了一个 Watcher 后台常驻进程,它在用户打开页面之前就已经跑完了分析:

# watcher.py 核心循环(基于实际代码)class WatcherEngine:    def run_once(self):        # ① 轮询审批平台 API        items = fetch_approval_list(self.config)        for item in items:            bid = item['id']            # ② 去重:查 SQLite 是否已分析            cur = self.db.execute(                'SELECT bill_id FROM approvals WHERE bill_id = ?', (bid,))            if cur.fetchone():                continue  # 已处理,跳过            # ③ 获取审批详情(表单字段)            context = fetch_approval_detail(self.config, bid)            # ④ 智能匹配 Skill(URL模式 + 关键词)            skill = match_skill(self.config, item.get('url', ''), context)            # 报价单 → 报价审批分析            # 合同   → 方案评估            # 财报   → 财报解读            # ⑤ 写入 pending,调用 Qoder AI            self.db.execute('''INSERT INTO approvals                (bill_id, url, title, skill, status, created_at)                VALUES (?, ?, ?, ?, 'pending', ?)''',                (bid, item['url'], item['title'], skill, now))            result = call_qoder_analysis(skill, context)            # ⑥ 更新分析结果            self.db.execute('''UPDATE approvals                SET result_text = ?, status = 'done', analyzed_at = ?                WHERE bill_id = ?''',                (result, datetime.now().isoformat(), bid))            self.db.commit()

当用户终于打开审批页面时,Chrome 扩展做的第一件事不是调 AI,而是先查缓存

// content_script.js — 缓存优先策略(基于实际代码)async function executeAutoTrigger(match) {  const { rule } = match;  const ctx = extractPageContext();  // 提取 billId、url、title  // ★ 先查后台 watcher 缓存  const cached = await checkWatcherCache(ctx.billId);  if (cached) {    console.log('★ 缓存秒开:', ctx.billId, cached.skill);    recordTrigger(ctx.billId, ctx.url, rule);    openPanel('auto');    setTimeout(() => sendCachedResult(cached), 500);    return;  // 不走实时分析  }  // 缓存未命中 → 去重 → 实时分析  if (!checkDedup(ctx.billId, ctx.url, rule)) return;  recordTrigger(ctx.billId, ctx.url, rule);  openPanel('auto');  // panel 加载后通过 bridge.py 触发实时 SSE 分析  sendToBackground({    type: 'AUTO_TRIGGER',    data: { skill: rule.skill, context: ctx }  });}

💡 设计要点:缓存命中的时候,面板显示”⚡ 缓存命中 · Skill「报价审批分析」· 3分钟前已分析”,用户看到的是毫秒级加载。只有 Watcher 覆盖不到的新类型审批单,才会走实时分析通路。

先进二:三层处理人校验

审批流程有明确的处理人概念——一个审批单到张三手里,李四打开看只是”浏览”,不应该触发任何 AI 分析。但大多数”自动触发”方案都忽略了这个校验。

我设计了三层校验,层层递进:

// content_script.js — 三策略提取当前处理人(基于实际代码)function extractPageHandler() {  const bodyText = (document.body?.innerText || '');  // 策略① CSS选择器:从配置中读取 selector 列表  for (const sel of handlerVerifyConfig.selectors) {    try {      const el = document.querySelector(sel);      if (el) {        const text = (el.innerText || el.textContent || '').trim();        if (text && text.length < 30) {          return text;  // "张三"        }      }    } catch (_) {}  }  // 策略② 正则匹配:从配置中读取 pattern 列表  for (const pattern of handlerVerifyConfig.textPatterns) {    try {      const m = bodyText.match(new RegExp(pattern, 'i'));      if (m && m[1]) {        const name = m[1].trim();        if (name && name.length < 20) return name;      }    } catch (_) {}  }  // 策略③ 按钮兜底:配置中指定的审批操作按钮可见 → 当前用户是处理人  for (const sel of handlerVerifyConfig.buttonSelectors) {    if (sel.includes(':contains(')) {      const text = sel.match(/:contains\('(.+?)'\)/)?.[1] || '';      const buttons = document.querySelectorAll(        'button, a, .btn, [role="button"]');      for (const btn of buttons) {        if ((btn.innerText || '').includes(text)            && btn.offsetParent !== null) {          return '__BUTTON_DETECTED__';        }      }    }  }  return null;}

拿到处理人后,和配置的用户名做匹配:

// content_script.js — 处理人校验(基于实际代码)function isCurrentHandler() {  const handler = extractPageHandler();  if (!handler)    return { isHandler: false, extractedName: null,             reason: '未检测到处理人信息' };  if (handler === '__BUTTON_DETECTED__')    return { isHandler: true, extractedName: '按钮检测',             reason: '审批操作按钮可见' };  // 名称匹配(支持别名,含 .filter(Boolean) 过滤空值)  const names = [currentUser.userName, ...currentUser.aliases]    .map(n => n.toLowerCase().trim()).filter(Boolean);  const lowerHandler = handler.toLowerCase();  const matched = names.some(name =>    lowerHandler.includes(name) || name.includes(lowerHandler));  return matched    ? { isHandler: true, extractedName: handler, reason: '名称匹配' }    : { isHandler: false, extractedName: handler,        reason: `页面处理人「${handler}」不是当前用户「${currentUser.userName}」` };}

在触发链路的最前面调用:

async function executeAutoTrigger(match) {  // ★ 处理人校验:不是当前处理人 → 直接跳过  const check = isCurrentHandler();  if (!check.isHandler) {    console.log('⊘ 跳过:', check.reason);    return;  // 静默退出,不弹面板  }  // ... 后续缓存查询和AI分析 ...}

⚠️ 为什么这个很重要:没有这个校验,同事打开你的审批单看热闹,也会触发一次 AI 分析(消耗 Token、浪费算力)。更关键的是,如果分析结果面板自动弹出,会干扰非处理人的浏览体验。

先进三:事件驱动 + 多层去重

审批页面往往是 SPA(单页应用),URL 可能不变但内容已切换。同时页面 DOM 可能多次渲染,同一个审批单可能被重复触发。这需要一套健壮的事件监听 + 去重机制:

// content_script.js — 自动触发引擎(基于实际代码)// ① URL 变化监听(SPA路由切换)function startUrlWatcher() {  let lastUrl = location.href;  new MutationObserver(() => {    if (location.href !== lastUrl) {      lastUrl = location.href;      onPageChanged();  // URL变了→重新匹配规则    }  }).observe(document, { subtree: true, childList: true });}// ② DOM 内容监听(审批数据异步加载)function startDomWatcher() {  new MutationObserver(() => {    const match = matchTriggerRule();    if (match) executeAutoTrigger(match);  }).observe(document.body, { childList: true, subtree: true });}// ③ 多层去重(真实实现)function checkDedup(billId, url, rule) {  const now = Date.now();  // 层1:全局冷却窗口  if (now - lastAutoTriggerTime < cooldownMs) {    return false;  }  // 层2:单号去重(5分钟窗口)  if (billId && billId !== 'UNKNOWN') {    const billKey = 'bill:' + billId + ':' + (rule.skill || rule.name);    if (dedupCache[billKey]        && (now - dedupCache[billKey] < dedupWindowMs)) {      return false;    }  }  // 层3:URL + Skill 去重(SPA同页面不重复触发)  const urlKey = 'url:' + url + ':' + (rule.skill || rule.name);  if (triggeredUrls.has(urlKey)) {    return false;  }  return true;}

先进四:数据不出本地

这是整套方案最”反潮流”的地方——所有 AI 推理在用户自己的电脑上完成。审批单数据不发送到任何云端服务。

审批平台 API → watcher.py → bridge.py → qodercli (本地AI)     ↑                                              │     │          数据全在用户本机                       │     │          SQLite 缓存也在本地磁盘                │     └──────────────────────────────────────────────┘

对比主流方案:

┌──────────────┬──────────────┬──────────────┐│  方案        │  数据流向    │  隐私风险    │├──────────────┼──────────────┼──────────────┤│  ChatGPT插件 │  审批数据→云端│  高(涉密)  ││  Copilot     │  上下文→云端 │  中          ││  本方案      │  全在本地    │  无          │└──────────────┴──────────────┴──────────────┘

这对处理合同、报价、人事审批等敏感数据尤为重要。

· · ·

四、Skill 路由引擎:什么单子配什么分析

不同类型的审批需要不同的分析思路。报价单要看价格合理性、历史对比、汇率影响。合同要看条款风险、违约责任。简历要看匹配度、关键经验。所以设计了一套规则引擎,支持 URL 模式匹配 + 关键词匹配:

// config/auto-trigger-rules.json — Skill 路由规则(基于实际配置){  "rules": [    {      "id": "price-approval",      "name": "报价审批自动分析",      "triggers": [        { "type": "urlContains", "value": "/price-approval" },        { "type": "keyword",          "keywords": ["报价审批","报价单","折扣率","销售报价"],          "minHits": 2 }      ],      "skill": "报价审批分析"    },    {      "id": "contract-review",      "triggers": [        { "type": "urlContains", "value": "/contract" },        { "type": "keyword",          "keywords": ["合同","甲方","乙方","协议","违约责任"],          "minHits": 2 }      ],      "skill": "方案评估"    },    {      "id": "financial-statement",      "triggers": [        { "type": "keyword",          "keywords": ["利润表","损益表","资产负债表","现金流量表"],          "minHits": 1 }      ],      "skill": "财报解读"    },    {      "id": "resume-screening",      "triggers": [        { "type": "keyword",          "keywords": ["简历","求职","应聘","工作经历","教育背景"],          "minHits": 2 }      ],      "skill": "简历筛选助手"    }  ],  "defaultSkill": "审批快速分析",  "cooldownMs": 30000,  "dedupWindowMs": 300000}

每条规则包含触发条件(URL 匹配 OR 关键词命中)和目标 Skill。未匹配到的走默认 Skill。规则是纯 JSON 配置,加新审批类型不需要改代码,只加一条 JSON 记录即可。

· · ·

五、如何搭建你自己的?

整个系统部署只涉及三个东西跑起来:

第一步:配置审批平台 API

编辑 watcher/config.json,填上你公司审批系统的 API 地址和 Token:

{  "user": {    "userId": "zhangsan",    "userName": "张三",    "userNameAliases": ["Zhang San"]  },  "apis": {    "list": {      "url": "https://your-oa.com/api/approvals/pending",      "headers": { "Authorization": "Bearer YOUR_TOKEN" },      "responseMapping": {        "itemsPath": "data.list",        "idField": "id",        "handlerField": "currentHandler"      }    },    "detail": {      "urlPattern": "https://your-oa.com/api/approvals/{id}",      "responseMapping": {        "fieldsPath": "formData",        "handlerField": "currentHandler"      }    }  }}

💡 关键:responseMapping 用点路径(如 data.list)描述 API 返回 JSON 中审批列表的位置。换一个审批平台只需改这几行配置,watcher 代码不用动。

第二步:配置处理人校验

打开你审批平台的任意一个审批页面,按 F12 查看 DOM 结构,找到”当前处理人”对应的 HTML。比如你的平台是这样的:

<div class="approval-info">  <span class="label">当前处理人</span>  <span class="value user-name">张三</span></div>

那就把 .user-name 填到 config/auto-trigger-rules.json

{  "currentUser": {    "userId": "zhangsan",    "userName": "张三",    "userNameAliases": ["Zhang San"]  },  "handlerVerification": {    "enabled": true,    "selectors": [".user-name", ".approval-handler"],    "textPatterns": [      "当前处理人[::]*\\s*(\\S+)",      "审批人[::]*\\s*(\\S+)"    ]  }}

第三步:启动服务

# 终端1:启动 Qoder 桥接服务(在原 James超级助理 项目目录下)cd ../james-super-assistant && python bridge.py# → HTTP 服务启动在 :39877# 终端2:启动后台 Watcher(在 approval-auto-agent 目录下)cd watcher && python watcher.py# → 轮询服务启动,结果 HTTP :39878# Chrome:加载扩展(开发者模式 → 加载已解压的扩展 → 选项目根目录)

第四步:打开审批页面

正常使用审批系统即可。当审批单到你手里时,右侧自动弹出面板,分析结果已经在那里了。

· · ·

六、成本与收益

┌──────────────┬─────────────────────────────┐│  指标        │  数据                       │├──────────────┼─────────────────────────────┤│  打开到结果  │  缓存命中 <100ms             ││              │  实时分析 15~60s             ││  预分析延迟  │  审批单创建后 ~30s 完成      ││  隐私        │  全程本地,0 数据上传        ││  扩展性      │  新审批类型仅加JSON配置      ││  运行成本    │  本地算力,无 API 费用          │└──────────────┴─────────────────────────────────┘

· · ·

总结

整套方案的核心思路:用后台轮询把 AI 分析前置,用缓存先行把等待时间归零,用三层校验确保只在处理人打开时触发

· · ·