PageAgent-住在网页里的 AI 操控员

一、从一个问题说起:为什么需要"页面内"的 AI Agent?

过去两年,浏览器自动化领域热闹非凡。browser-use、Playwright MCP、各类 Headless 方案层出不穷,但它们都有一个共同特征------需要一个"外部大脑":Python 后端、无头浏览器实例、或浏览器扩展的特殊权限。

阿里巴巴开源的 PageAgent 提出了一个极为简洁的逆向思路:不从外部操控浏览器,让 AI Agent 直接"住在"网页里。 一行 <script> 标签,Agent 就在当前页面的 JavaScript 上下文中运行------不要 Python,不要无头浏览器,不要截图和多模态模型,甚至不要浏览器扩展。

下面这张图能直观地感受到区别:

xml 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    传统方案 vs PageAgent                         │
├─────────────────────────────┬───────────────────────────────────┤
│  browser-use / Playwright   │         PageAgent                 │
│                             │                                   │
│  ┌───────────┐              │  ┌─────────────────────────────┐  │
│  │ Python    │──WebSocket──▶│  │         你的网页              │  │
│  │ 后端服务   │  / CDP      │  │  ┌─────────────────────┐    │  │
│  └───────────┘              │  │  │  PageAgent (JS)     │    │  │
│       │                     │  │  │  ┌───────┐ ┌──────┐ │    │  │
│       ▼                     │  │  │  │ Agent │→│ DOM  │ │    │  │
│  ┌───────────┐              │  │  │  │ 循环  │ │ 操控 │ │    │  │
│  │ Headless  │              │  │  │  └───┬───┘ └──────┘ │    │  │
│  │ Browser   │              │  │  │      │  ↕ LLM API   │    │  │
│  └───────────┘              │  │  └──────┼──────────────┘    │  │
│                             │  └─────────┼───────────────────┘  │
│  需要: Python + 无头浏览器    │  只需: 一行 <script> 标签          │
└─────────────────────────────┴───────────────────────────────────┘

这篇文章将从最简单的用法出发,逐层深入到源码架构的核心设计,配有丰富示例和图解,帮你完整理解 PageAgent 的工作原理。


二、实战示例:从入门到高级

🟢 入门级:一行代码,5 秒体验

如果你只想快速感受效果,把下面这行代码贴到任意网页的控制台或 HTML 里:

html 复制代码
<script src="https://cdn.jsdelivr.net/npm/page-agent@1.5.9/dist/iife/page-agent.demo.js" crossorigin="true"></script>

页面右下角会出现一个对话面板,输入自然语言指令即可操作页面。这个 Demo CDN 自带免费测试 LLM,开箱即用。

🟡 进阶级:NPM 集成 + 自选模型

实际项目中,你需要接入自己的 LLM:

js 复制代码
import { PageAgent } from 'page-agent'

const agent = new PageAgent({
  model: 'qwen3.5-plus',
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
  apiKey: 'YOUR_API_KEY',
  language: 'zh-CN',
})

// 方式一:程序化执行
const result = await agent.execute('在搜索框输入 "iPhone 16",然后点击搜索按钮')
console.log(result.success)  // true / false
console.log(result.data)     // Agent 的执行总结

// 方式二:弹出对话面板,让用户自行输入
agent.panel.show()

支持的模型非常丰富------OpenAI GPT 系列、Claude、Qwen、DeepSeek、Gemini、Grok、MiniMax、Kimi、GLM,甚至通过 Ollama 本地部署的开源模型都可以。只要兼容 OpenAI 的 /chat/completions 接口即可。

🟡 进阶级:知识注入------让 AI 懂你的业务

裸用 Agent 时,它只知道页面上有什么元素,但不了解你的业务规则。通过 instructions 你可以注入领域知识:

js 复制代码
const agent = new PageAgent({
  // ...LLM config
  instructions: {
    // 全局指令:所有页面生效
    system: `
      你是一个专业的电商运营助手。
      规则:
      - 提交订单前必须先确认价格和数量
      - 遇到错误时立即停止,不要盲目重试
      - 优先使用筛选器缩小搜索范围
    `,
    // 页面级指令:根据 URL 动态返回
    getPageInstructions: (url) => {
      if (url.includes('/checkout')) {
        return '这是结算页面。请先核对收货地址,再检查是否有优惠券可用。'
      }
      if (url.includes('/products')) {
        return '这是商品列表页。先使用左侧筛选器缩小范围,再帮用户选择商品。'
      }
      return undefined
    }
  }
})

指令的工作方式如下图所示:

xml 复制代码
每一步执行前,prompt 的组装结构:

┌────────────────────────────────────────┐
│  <instructions>                        │
│    <system_instructions>               │
│      你是电商运营助手...                  │
│    </system_instructions>              │
│    <page_instructions>                 │  ← 仅当 URL 匹配时才出现
│      这是结算页面...                     │
│    </page_instructions>                │
│  </instructions>                       │
│                                        │
│  <agent_state>                         │
│    用户请求 + 步数信息                    │
│  </agent_state>                        │
│                                        │
│  <agent_history>                       │
│    之前每步的反思 + 动作结果               │
│  </agent_history>                      │
│                                        │
│  <browser_state>                       │
│    当前页面 URL、可交互元素、滚动位置       │
│  </browser_state>                      │
└────────────────────────────────────────┘

🟡 进阶级:数据脱敏------敏感信息不出页面

在把页面内容发送给 LLM 之前,transformPageContent 钩子允许你过滤敏感数据:

js 复制代码
const agent = new PageAgent({
  // ...LLM config
  transformPageContent: async (content) => {
    // 手机号脱敏:138****1234
    content = content.replace(/\b(1[3-9]\d)(\d{4})(\d{4})\b/g, '$1****$3')
    // 邮箱脱敏
    content = content.replace(
      /\b([a-zA-Z0-9._%+-])[^@]*(@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\b/g,
      '$1***$2'
    )
    // 银行卡号脱敏
    content = content.replace(/\b(\d{4})\d{8,11}(\d{4})\b/g, '$1********$2')
    return content
  }
})

LLM 看到的是脱敏后的内容,但页面上的真实数据不受影响,Agent 的操作仍然作用于原始 DOM 元素。

🔴 高级:自定义工具------给 AI 接上后端 API

内置工具只能操作 DOM,但通过 customTools 你可以让 Agent 调用任意业务接口:

js 复制代码
import { z } from 'zod/v4'
import { PageAgent, tool } from 'page-agent'

const agent = new PageAgent({
  // ...LLM config
  customTools: {
    // 添加购物车工具:AI 可以直接调 API 而非点按钮
    add_to_cart: tool({
      description: '通过商品 ID 添加到购物车',
      inputSchema: z.object({
        productId: z.string(),
        quantity: z.number().min(1).default(1),
      }),
      execute: async function (input) {
        await fetch('/api/cart', {
          method: 'POST',
          body: JSON.stringify(input),
        })
        return `✅ 已添加 ${input.quantity} 件 ${input.productId} 到购物车`
      },
    }),

    // 搜索知识库工具:让 AI 先查资料再操作
    search_kb: tool({
      description: '搜索内部知识库',
      inputSchema: z.object({
        query: z.string(),
        limit: z.number().max(10).default(3),
      }),
      execute: async function (input) {
        const res = await fetch(`/api/kb?q=${encodeURIComponent(input.query)}&limit=${input.limit}`)
        return JSON.stringify(await res.json())
      },
    }),

    // 移除内置工具:比如禁止 AI 向用户提问
    ask_user: null,
  },
})

🔴 高级:完全自定义 UI(React 示例)

不想用内置面板?核心逻辑和 UI 完全解耦,你可以用 React/Vue/任何框架搭建自己的界面:

jsx 复制代码
import { PageAgentCore } from '@page-agent/core'
import { PageController } from '@page-agent/page-controller'
import { useState, useEffect } from 'react'

// 1. 自定义 React Hook 监听 Agent 事件
function useAgent(agent) {
  const [status, setStatus] = useState(agent.status)
  const [history, setHistory] = useState(agent.history)
  const [activity, setActivity] = useState(null)

  useEffect(() => {
    const onStatus = () => setStatus(agent.status)
    const onHistory = () => setHistory([...agent.history])
    const onActivity = (e) => setActivity(e.detail)

    agent.addEventListener('statuschange', onStatus)
    agent.addEventListener('historychange', onHistory)
    agent.addEventListener('activity', onActivity)
    return () => {
      agent.removeEventListener('statuschange', onStatus)
      agent.removeEventListener('historychange', onHistory)
      agent.removeEventListener('activity', onActivity)
    }
  }, [agent])

  return { status, history, activity }
}

// 2. 创建无 UI 的 Core Agent
const agent = new PageAgentCore({
  pageController: new PageController({ enableMask: true }),
  baseURL: 'https://api.openai.com/v1',
  apiKey: 'your-key',
  model: 'gpt-5.1',
})

// 3. 你的自定义 UI 组件
function MyAgentPanel() {
  const { status, history, activity } = useAgent(agent)

  return (
    <div className="my-agent-ui">
      <div>状态: {status}</div>
      {activity?.type === 'thinking' && <div>🧠 思考中...</div>}
      {activity?.type === 'executing' && <div>⚡ 执行: {activity.tool}</div>}
      {history.filter(e => e.type === 'step').map((step, i) => (
        <div key={i}>步骤 {i+1}: {step.action.name} → {step.action.output}</div>
      ))}
    </div>
  )
}

🔴 高级:对接外部 Agent 系统

把 PageAgent 作为工具注册到你现有的 AI 客服/助手系统中:

js 复制代码
// 你的主 Agent 系统中
const pageAgentTool = {
  name: 'operate_webpage',
  description: '在当前网页上执行操作,如点击、填写表单、查询信息',
  parameters: {
    type: 'object',
    properties: {
      instruction: { type: 'string', description: '操作指令' }
    },
    required: ['instruction']
  },
  execute: async (params) => {
    const result = await pageAgent.execute(params.instruction)
    return { success: result.success, message: result.data }
  }
}

// 注册到你的 Agent 框架...

这样你的客服机器人就不再只会说"请点击左上角的设置按钮",而是直接帮用户操作。


三、Monorepo 架构全景图

PageAgent 采用 monorepo 结构,packages/ 下 7 个子包分层清晰:

javascript 复制代码
┌─────────────────────────────────────────────────────────────┐
│                      用户代码                                │
│              import { PageAgent } from 'page-agent'         │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│    📦 page-agent (门面层,28行代码)                          │
│    组装 Core + PageController + UI Panel                     │
└───────┬───────────────────┬──────────────────┬──────────────┘
        │                   │                  │
        ▼                   ▼                  ▼
┌──────────────┐  ┌──────────────────┐  ┌────────────┐
│  📦 core     │  │  📦 page-        │  │  📦 ui     │
│  Agent 循环   │  │   controller     │  │  交互面板   │
│  提示词工程   │  │  DOM 提取与简化   │  │            │
│  工具系统     │  │  元素动作模拟     │  │            │
│  AutoFixer   │  │  遮罩层管理       │  │            │
└──────┬───────┘  └──────────────────┘  └────────────┘
       │
       ▼
┌──────────────┐
│  📦 llms     │     📦 extension (可选)
│  OpenAI 协议  │     Chrome 扩展,多标签页
│  模型补丁     │
│  重试机制     │     📦 website
└──────────────┘     官方文档站

核心设计原则:core 不依赖 uipage-controller 不依赖 core,任何一层都可以独立替换。想换 UI?用 PageAgentCore 监听事件自己画。想换 DOM 操作方式?实现 PageController 接口即可。


四、核心引擎:Re-Act Agent 循环

PageAgent 的灵魂在 PageAgentCore 类中。它实现了经典的 Re-Act(Reasoning + Acting)循环

4.1 一次任务的完整生命周期

arduino 复制代码
agent.execute("填写上周五出差的报销单")
         │
         ▼
┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
│          while (step < maxSteps)                          │
│                                                           │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐            │
│  │ 1.Observe│───▶│ 2.Think  │───▶│ 3.Act    │──┐         │
│  │ 观察页面  │    │ LLM 推理  │    │ 执行动作  │  │         │
│  └──────────┘    └──────────┘    └──────────┘  │         │
│       ▲                                        │         │
│       │           ┌──────────┐                 │         │
│       └───────────│ 4.Record │◀────────────────┘         │
│                   │ 记录历史  │                            │
│                   └──────────┘                            │
│                        │                                  │
│               action == 'done'?                           │
│                 ├── Yes → 返回结果                          │
│                 └── No  → 继续循环                          │
└─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘

第一阶段 ObservePageController.getBrowserState() 扫描 DOM 树,提取所有可交互元素并编号索引,输出一份 LLM 可读的简化文本。同时进行环境感知------URL 是否变化?累计等待时间是否过长?剩余步数是否告急?这些观察被推入历史流。

第二阶段 Think :系统提示词 + 用户提示词 + 浏览器状态 + 完整历史事件被一起发送给 LLM。这里有一个核心设计------MacroTool(详见下节)。

第三阶段 Act:从 LLM 输出中解析出动作名和参数,通过 PageController 在页面上执行真实的 DOM 操作。

第四阶段 Record :执行结果、LLM 的反思内容、token 用量等打包成 AgentStepEvent 推入历史数组,下一轮循环时回传给 LLM 形成连续记忆。

循环终止条件有三个:LLM 调用 done(任务完成)、步数超过 maxSteps(默认 40)、或不可恢复错误。

4.2 MacroTool:强制"先想后做"

传统方案让 LLM 从多个工具中自由选择。PageAgent 走了一条不同的路------把所有工具合并 成一个叫 AgentOutput 的巨型工具:

css 复制代码
┌────────────────────────────────────────────────┐
│              MacroTool: AgentOutput             │
│                                                │
│  {                                             │
│    evaluation_previous_goal: "上一步成功了...",   │  ← 反思
│    memory: "已找到搜索框,index=5...",            │  ← 记忆
│    next_goal: "在搜索框输入关键词",               │  ← 规划
│    action: {                                   │
│      input_text: { index: 5, text: "iPhone" }  │  ← 动作
│    }                 ▲                         │
│  }                   │                         │
│                      │                         │
│       action 字段是所有内置工具的联合类型:          │
│       click_element_by_index | input_text |     │
│       scroll | select_dropdown_option |         │
│       wait | done | ask_user | ...              │
└────────────────────────────────────────────────┘

源码中用 Zod 的 z.union 将所有工具的 inputSchema 合并成 action 字段的类型。LLM 每次调用 AgentOutput 时,必须同时输出反思和具体动作。这种设计大幅减少了"冲动行为"------Agent 不会跳过思考直接行动。

4.3 两条事件流:记忆 vs 反馈

scss 复制代码
┌───────────────────────────────────────────────────────┐
│                  PageAgentCore                        │
│                                                       │
│   Historical Events                Activity Events    │
│   (historychange)                   (activity)        │
│                                                       │
│   ┌─────────────┐                ┌─────────────┐     │
│   │ step        │                │ thinking    │     │
│   │ observation  │                │ executing   │     │
│   │ user_takeover│                │ executed    │     │
│   │ retry       │                │ retrying    │     │
│   │ error       │                │ error       │     │
│   └──────┬──────┘                └──────┬──────┘     │
│          │                              │             │
│    持久化 │ 传给 LLM               瞬态 │ 仅 UI 用     │
│          ▼                              ▼             │
│   agent.history[]               UI 状态动画/loading    │
└───────────────────────────────────────────────────────┘

History Events 构成 Agent 的"记忆",每轮都发送给 LLM。Activity Events 是瞬态 UI 反馈("正在思考"/"正在点击按钮"),不进入 LLM 上下文。这种分离保证了 LLM 的上下文始终干净。


五、DOM 翻译官:不靠截图的页面理解

5.1 纯文本路线 vs 截图路线

css 复制代码
截图路线 (Claude Computer Use 等)        文本路线 (PageAgent)
                                        
  页面 → 截图 → 多模态LLM               页面 → DOM树 → 简化文本 → 文本LLM
                                        
  ✓ 能看到图片/Canvas                    ✗ 看不到图片/Canvas
  ✗ 需要多模态模型                       ✓ 普通文本模型即可
  ✗ 截图=更多token≈更贵                   ✓ token 更少更便宜
  ✗ 需要特殊权限                         ✓ 零权限

对于大多数 SaaS 后台、表单填写、数据录入场景,文本路线是极为务实的选择。

5.2 DOM 提取:从真实页面到 LLM 可读文本

PageController.getBrowserState() 是整条链路的入口。它的内部流程:

ini 复制代码
真实 DOM 树
    │
    ▼  getFlatTree()
遍历 DOM,识别可交互元素,分配数字索引
标记新出现的元素 (WeakMap 缓存)
    │
    ▼  flatTreeToString()
转换为 LLM 友好的文本格式
    │
    ▼  组装 BrowserState
    
  header: "Current Page: [商品列表](https://...)
           Page info: 1920x1080px viewport...
           [Start of page]"

  content: "[0]<a aria-label=首页 />
            [1]<input placeholder=搜索商品... />
            [2]<button>搜索</button>
            今日推荐
            *[3]<div>iPhone 16 Pro ¥7999</div>     ← * 号表示新出现
            *[4]<button>加入购物车</button>
            [5]<select>选择颜色</select>"

  footer: "... 1200 pixels below (2.3 pages) - scroll to see more ..."

flatTreeToString 做了大量优化细节:去除重复属性(aria-label 与文本内容相同时只保留一个)、截断过长属性、标注可滚动容器的滚动距离、缩进表示 DOM 层级关系。

5.3 动作模拟:为什么不用 .click()

简单调用 element.click() 在很多前端框架中不能正确触发事件。PageAgent 的 clickElement 模拟了完整的用户行为链:

scss 复制代码
clickElement(element) 的执行序列:

  scrollIntoView    ← 确保元素可见
       ↓
  movePointerTo     ← 移动指针到元素中心(触发UI动画)
       ↓
  mouseenter        ← 模拟鼠标进入
  mouseover
       ↓
  mousedown         ← 模拟按下
       ↓
  focus             ← 聚焦(确保 React 等框架的事件能触发)
       ↓
  mouseup           ← 模拟释放
       ↓
  click             ← 最终点击事件

文本输入更复杂------对 contenteditable 富文本编辑器,按顺序派发 beforeinput(清空)→ 修改 innerText → 派发 input(插入),以兼容 React 受控组件和 Quill 等编辑器。对普通 input/textarea,则使用原生 value setter 绕过框架拦截,再手动触发 input 事件。


六、LLM 层:兼容万家,容错为先

6.1 OpenAI 兼容协议统一天下

@page-agent/llms 没引入任何 LLM SDK,直接用 fetch 调 /chat/completions 接口。如今几乎所有主流模型商都支持这套协议,因此 PageAgent 天然兼容数十种模型。

6.2 模型补丁:实战踩坑的结晶

源码中的 modelPatch 函数根据模型名称动态调整请求参数:

ini 复制代码
模型                    补丁内容
─────────────────────────────────────────────────
Qwen 系列      →  temperature ≥ 1.0,关闭 thinking
Claude 系列    →  tool_choice 格式转换为 Claude 风格
Grok 系列      →  删除 tool_choice,禁用 reasoning
GPT-5 系列     →  reasoning_effort = 'low'
GPT-5-mini     →  reasoning_effort = 'low', temperature = 1
Gemini 系列    →  reasoning_effort = 'minimal'
MiniMax 系列   →  temperature 钳位到 (0, 1],删除 parallel_tool_calls

这些全是真实环境下踩坑后的总结,对多模型兼容开发极有参考价值。

6.3 AutoFixer:当 LLM 不守规矩时

不同 LLM 的输出格式千差万别。normalizeResponse 穷举了各种异常并逐一修复:

css 复制代码
LLM 的常见"不规矩"输出              AutoFixer 的修复

把 JSON 放在 content 里              → 提取 JSON,包装成 tool_calls
而不是 tool_calls

返回动作层级而非                     → 包装一层 { action: ... }
AgentOutput 完整结构

双重 JSON 字符串化                    → 递归 JSON.parse
"{ \"action\": \"...\" }"

原始值输入                            → 根据 Zod schema 推断字段名
{ click_element_by_index: 2 }         → { click_element_by_index: { index: 2 } }

content 里还套了一层                   → 解析嵌套的 function 结构
function wrapper

这套容错机制是 PageAgent 能稳定兼容这么多模型的关键原因之一。


七、提示词工程:Agent 的"岗位说明书"

系统提示词(system_prompt.md)详细规定了 Agent 的输入格式、行为准则和能力边界。几个值得注意的设计:

输入格式约定 :交互元素的格式是 [index]<type>text</type>,只有带数字索引的元素才可操作。新出现的元素用 *[ 标记。缩进表示 DOM 层级。

行为规则亮点:不要重复同一动作超过 3 次;输入文本后如果被中断,很可能弹出了建议列表(要去选择);遇到验证码告知用户无法解决;区分"精确步骤"和"开放式任务"两种模式。

"示弱"设计------这是最有意思的部分:明确告知 LLM "可以失败"、"用户可能是错的"、"网页可能有 bug"、"过度尝试可能有害"。避免 Agent 在无法完成任务时陷入无意义的死循环。


八、生命周期钩子:完整的可观测性

arduino 复制代码
onBeforeTask ──▶ ┌───────────────────────────────┐
                 │  onBeforeStep ──▶ step ──▶ onAfterStep  │  × N 步
                 └───────────────────────────────┘
onAfterTask  ◀── 返回 ExecutionResult { success, data, history }

onDispose    ◀── agent.dispose()

配合 transformPageContent(数据脱敏)和 customSystemPrompt(完全自定义提示词),开发者拥有对 Agent 行为的完全控制权。


九、使用限制:诚实面对能力边界

PageAgent 选择了"纯文本 DOM"路线,这意味着:

能做的:点击、文本输入、下拉选择、表单提交、页面滚动、焦点切换、执行 JavaScript。

做不到的:悬停(hover)、拖拽、右键菜单、键盘快捷键、坐标定位操作、图片/Canvas/WebGL/SVG 等视觉内容识别、Monaco/CodeMirror 等特殊编辑器。

语义化的 HTML 和良好的可访问性(ARIA 标签等)会显著提升 Agent 效果。反常识的交互逻辑、纯视觉的操作提示则会降低成功率。


十、总结:一个务实的工程决策

通读源码后,PageAgent 的核心设计哲学可以归纳为三个词:

务实------纯文本 DOM 而非截图,牺牲视觉理解换来对普通模型的兼容性和更低的成本。MacroTool 强制"先想后做",在可控性和灵活性之间找到平衡。

容错------从 AutoFixer 对畸形输出的修复,到 modelPatch 对不同模型的适配,到提示词中鼓励"可以失败",整个系统对不确定性有很高包容度。

解耦------Core、PageController、UI、LLMs 四层分明,任何一层可独立替换。你可以只用 Core 做无头自动化,也可以换上自己的 React UI,还可以把它嵌入你现有的 Agent 系统作为"手和眼"。

对于 SaaS 开发者想快速给产品加 AI Copilot、企业想做管理后台的智能化改造、或者无障碍增强场景,PageAgent 提供了目前门槛最低的入口------一行 <script> 标签,你的网页就有了一个 AI 操作员。

相关推荐
王小酱2 小时前
A2UI 深度解读:让 AI Agent "说出"用户界面的开放协议
openai·ai编程·aiops
米小虾2 小时前
hackerbot-claw 攻击事件深度解析:AI Agent 时代的安全警钟
github·ai编程
进击的野人3 小时前
Prompt工程入门指南:写给AI学习新手的提示词秘籍
人工智能·aigc·ai编程
甲维斯3 小时前
用完火山,腾讯,阿里的编程模型,我失眠了!
ai编程
树獭叔叔3 小时前
别再盲目堆残差了!Moonshot AI 的 AttnRes 如何让 LLM 训练提速 25%?
后端·aigc·openai
码路飞3 小时前
GPT-5.4 mini 和 nano 昨天刚发,我连夜测了一下,说说真实感受
gpt·openai·api
KevinZhang135794 小时前
第 8 节:集成 CubeJS 数据模型
ai编程·vibecoding
晨欣4 小时前
如何根据 config.json 核对 MoE 模型的激活参数:以 gpt-oss-120b 为例(GPT-5.4-high 生成)
gpt·大模型·json·openai
一块小方糖4 小时前
OA工时填报Skill,打通gitlab与禅道,实现每日工时自动提交
ai编程