钉钉开放平台的第一用户不再是开发者,而是开发者的 Agent

Agent Experience is the new Developer Experience

昨晚我用 OpenCode 在钉钉上写一个会议通知 Agent。需求很简单:查日历找到明天的会议,给参会人发一条钉钉消息。

OpenCode 读完了 DWS CLI 的 help 文档,开始生成调用代码。它要发消息,看到了三个命令:dws chat message senddws chat message send-by-botdws chat message send-by-webhook。它选了 send,传了 --group--text,没传 --title

返回了一个错误:「发群服务窗会话消息失败」。

OpenCode 懵了。我也懵了。什么「服务窗」?我在发群消息,跟服务窗有什么关系?

后来我翻了 dws chat message send --help,在最底下发现了一行小字:

--title 是消息标题,群聊与单聊都必填(API 强制要求;缺失时返回误导性的「发群服务窗会话消息失败」)。

平台团队知道这个错误信息是误导性的,他们选择在 help 文档里标注「误导性」,而不是修复 API 的返回。

但这还没完。OpenCode 继续工作,又遇到了第二个问题:send 命令有三个互斥的目标参数——--group(群聊)、--user(userId)、--open-dingtalk-id(openDingTalkId)。help 文档说「三者只能选其一」,但没有解释什么时候该用哪个。userIdopenDingTalkId 的区别是什么?Agent 无从推理——它只能猜,猜错了再换。

这整个过程花了十几分钟。但这十几分钟本来不应该存在。

这不是假设场景,这是 DWS 今天的真实状态。

DX → AX:开放平台用户变迁——从人类开发者到 Coding Agent

一个范式迁移正在发生

过去十年,开放平台的核心 KPI 是 DX(Developer Experience):文档好不好读、SDK 好不好用、社区活不活跃。所有这些优化的对象都是同一个: 人类开发者

但现在,人类开发者写代码的方式变了。

Cursor、Claude Code、Codex、Windsurf——AI Coding 工具已经不是少数极客的玩具,而是主流生产力工具。开发者越来越多地扮演 reviewer 和 architect 的角色,而实际写代码的是 Agent。

这意味着开放平台的「第一用户」正在悄悄换人:

旧范式(DX):
    人类开发者 → 读文档 → 理解参数 → 写代码 → 调试

新范式(AX):
    开发者 → 指挥 Coding Agent → Agent 读 Schema → Agent 推理参数 → Agent 生成代码 → 开发者 review

在旧范式里,平台服务好人类开发者就够了。在新范式里,平台必须服务好开发者的 Agent。

这就是我说的 AX(Agent Experience): 参数设计让 Agent 一看就懂,Schema 表达力让 Agent 能自主推理,错误码让 Agent 能自动恢复。

AX ⊃ DX,不是 AX vs DX

有人可能会说:「API 设计对人类友好就够了。人能理解就行,Agent 不需要特别优化。」

这个观点在 AI Coding 之前成立。但它忽略了一个关键差异: 人类能容忍歧义,Agent 不能。

维度人类开发者Coding Agent
错误信息「发群服务窗会话消息失败」搜文档,发现缺 --title完全无法推理——错误信息与实际问题毫无关联
三个互斥参数 --group / --user / --open-dingtalk-id查文档搞清楚区别「三者只能选其一」——但没有说什么时候该选哪个
userId vs openDingTalkId知道是两种 ID 体系都是 ID,为什么有两个?没有 Schema 约束能告诉它
--msg-type 留空默认 markdown记住这个约定Agent 不知道留空是什么格式,只能显式传值

人类开发者看到「发群服务窗会话消息失败」,会去搜文档或问同事,最终发现缺了 --title。Agent 看到同样的错误信息,没有任何上下文可以推理——错误说的是「服务窗」,实际问题是没有传标题。

对 Agent 友好的设计不是对人类友好的替代,而是超集。 错误信息自解释(Missing required parameter: --title 而非「发群服务窗会话消息失败」)、参数约束 Schema 化(互斥关系和选择条件由机器可解析)、ID 类型显式标注——这些对人类开发者同样更好,只是以前「人类能凑合」所以平台偷懒没做。

「这不就是好的 API 设计吗?」

我预判到最聪明的读者会反驳:「错误信息要准确、参数要有 Schema 约束——这不是什么新范式,这就是好的 API 设计。RESTful 社区十几年前就在说了。」

说得对。但这恰恰证明了我的观点。

好的 API 设计原则一直都在,只是过去「够用」的标准太低了。 「发群服务窗会话消息失败」这种误导性错误信息之所以长期存在,不是因为它好,而是因为人类开发者能凑合——搜文档、问同事、看 help 里那行「缺失时返回误导性」的注释,总能搞定。平台的 DX 指标(文档点击率、SDK 下载量、社区活跃度)不会因为一个误导性错误码而下降。

但 Agent 不能凑合。Agent 不会因为你的错误信息烂就去搜 Stack Overflow,它会直接陷入死循环——错误说的是「服务窗」,它去排查服务窗配置,永远找不到真正的原因(缺 --title)。

所以 AX 不是新发明,而是旧标准的强制升级。 过去你可以选择做「好的 API 设计」也可以不做——反正人类能忍。现在你做不了了——因为你的用户不再忍。

这不是范式迁移,而是容忍度归零。

参数设计的具体差异

说抽象没意思,看 DWS 的真实案例。

反面案例——「人类凑合能懂」的设计:

dws chat message send 有三个发消息的命令变体:

# 以当前用户身份发
dws chat message send --group <openConversationId> --title "提醒" --text "明天开会"

# 以机器人身份发
dws chat message send-by-bot --group <openConversationId> --robot-code <code> --title "提醒" --text "明天开会"

# 以 Webhook 发
dws chat message send-by-webhook --token <token> --title "提醒" --text "明天开会"
# generated by hugo AI

Agent 看到这三个命令,面对的问题是:

  1. 三个命令都叫 send,区别只在后缀——Agent 无法从命名推理出「当前用户身份」vs「机器人身份」vs「Webhook」的选择条件
  2. --group 在三个命令里含义不同——send 里是 openConversationId,send-by-bot 里也是 openConversationId,但 send-by-webhook 不需要群 ID(token 已经绑定了群)
  3. --title 是必填但 help 标注为可选——help 说「可选,未指定时从内容截取」,但实际上 API 强制要求,缺失时返回误导性错误

AX 友好的设计——「Agent 一看就懂」:

# 统一的发送命令,身份由 --as 参数决定
dws chat send \
  --to group:<openConversationId> \
  --as current-user \
  --title "提醒" \
  --text "明天开会"

# --as 可选值:current-user | bot:<robot-code> | webhook:<token>
# --to 格式:<type>:<id>,type 可选 group | user | open-dingtalk-id
# --title 必填,缺失时返回:Error: missing required parameter --title
# generated by hugo AI

差异在哪?

  • 一个命令而非三个——--as 参数把身份选择显式化,Agent 知道有三个选项
  • --to 带类型前缀——group:cid_xxx 明确告诉 Agent 这是群 ID,不需要在 --group / --user / --open-dingtalk-id 之间猜
  • 错误信息自解释——Error: missing required parameter --title 而非「发群服务窗会话消息失败」

这不是锦上添花,这是决定开发者的 Coding Agent 能不能自主完成开发的关键。

OpenAPI 3.1 的 Schema 表达力已经足够做到这些。关键是平台团队是否把「Agent 可推理」作为设计标准。

开发者的新角色:Harness 工程师

当 Agent 能自主理解 API 并完成编码,开发者的工作重心就变了。

我在 Harness 工程就是人类几十年前就有的工程纪律 中讨论过,Agent = Model + Harness。模型是发动机,Harness 是让发动机发挥最大效能的底盘、悬挂、遥测系统。

现在把这个公式放到开放平台的语境下:

开发者的任务不再是「调 API 写代码」,而是构建生产级 Agent 的 Harness。

这个 Harness 包含六大组件:

组件说明开放平台的角色
模型选择/训练为特定任务选最优模型,甚至蒸馏任务专用小模型不关心——开发者自己搞定
Agent Runtime编排、工具调用、错误恢复、多 Agent 协作提供 Runtime 参考实现
评测集企业场景 benchmark,衡量任务完成度提供行业评测基准
知识库企业私有数据、SOP、历史案例不关心——企业自己搞定
反馈闭环用户反馈 → 数据回流 → 迭代提供数据回流接口
AutoResearch自动探索模型架构、蒸馏方案不关心——开发者自己搞定

注意这个表的右列: 开放平台不需要帮开发者做 Harness 的所有事,但需要提供连接能力和示范实现。 模型选择、知识库、AutoResearch 是开发者的核心竞争力——这恰恰是他们能做出差异化价值的地方。

开放平台的三层新定位

想清楚开发者的角色后,开放平台的定位就清晰了:

┌─────────────────────────────────────────────────────┐
│              企业生产级 Agent(开发者构建)              │
│                                                      │
│   模型选择    Agent Runtime    评测集                  │
│   知识库      反馈闭环         AutoResearch            │
│                                                      │
│   ← 这些是开发者的 Harness 工程,是核心竞争力 →         │
└──────────────────────┬──────────────────────────────┘
┌──────────────────────┴──────────────────────────────┐
│          开放平台(三层新定位)                         │
│                                                      │
│  ① 连接层:Agent-native 连接能力                      │
│     通讯录 | IM | 审批 | 日历 | 表格 | 文档 | 邮件     │
│     (参数自解释、Schema 可推理、错误码可自动恢复)       │
│                                                      │
│  ② 示范层:Reference Implementation                   │
│     鲁棒性模式 | 错误恢复模式 | 多 Agent 协作模式        │
│     (不是文档里的指南,是可以 fork 的代码)             │
│                                                      │
│  ③ 生态层:Agent 市场 + 模式库 + 评测基准              │
│     开发者共享成功的 Harness 模式                       │
└──────────────────────────────────────────────────────┘

工作流即软件,软件即 Agent 中,我讨论过企业真正缺的不是软件,而是把工作流重构成 AI Native 工作流。开放平台的连接层就是这个重构的基础设施——审批、表格、日历、通讯录,这些企业工作流的原生能力是 Slack 需要外部集成才能拼凑的,钉钉天生就有。

示范实现:展示「鲁棒性」长什么样

光给 API 不够。开放平台还需要回答一个开发者最关心的问题: 生产级 Agent 应该长什么样?

具体来说,开放平台应该提供可以直接 fork 的 Reference Implementation,展示四类最佳实践:

1. 鲁棒性示范

import time
from functools import wraps

def retry_with_backoff(max_retries=3, base_delay=1.0):
    """Agent-native 重试:指数退避 + 幂等保护"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except TransientError as e:
                    if attempt == max_retries - 1:
                        raise
                    delay = base_delay * (2 ** attempt)
                    time.sleep(delay)
        return wrapper
    return decorator
# generated by hugo AI

2. 错误恢复示范

Agent 遇到权限不足时不应该崩溃,应该降级:

def send_notification(recipient, content):
    """发送通知,权限不足时自动降级"""
    try:
        return dws.chat.send_message(
            recipient=recipient,
            content=content
        )
    except PermissionDeniedError:
        # 降级:发到 Agent 有权限的群,而非直接失败
        fallback_group = get_agent_default_group()
        return dws.chat.send_message(
            recipient={"type": "group_chat", "id": fallback_group},
            content=f"[转发] 原目标 {recipient['id']} 无权限: {content['text']}"
        )
# generated by hugo AI

3. 多 Agent 协作示范

FDE 的一天 中,我描述过 FDE 如何到客户现场快速构建 Agent。开放平台的示范实现应该把这种多 Agent 协作模式标准化——一个 Orchestrator Agent 拆解任务,多个 Specialist Agent 分别调用不同的 DWS 连接能力。

4. 安全合规示范

我在 Agent IAM 架构 中详细讨论过 Agent 身份注册、凭证边界注入、双轨审计的设计。示范实现应该把这些安全模式封装为开箱即用的中间件。

终极目标:超越通用 Agent

所有这些努力的终点是什么?

让开发者基于开放平台构建的企业 Agent,在特定场景下实现 Claude 同等甚至更好的任务完成度。

这个目标听起来大胆,但逻辑是清晰的:

Claude 的任务完成度 = 通用模型能力 + 通用工具调用

开发者的优势       = 任务专用模型   + 企业专属工具(DWS 连接层)
                     (小模型+蒸馏)    (审批/表格/OA/日历...)

                   = 成本更低 × 场景更深 × 合规性更好

通用 Agent 很强,但它不了解你企业的审批流程、你的组织架构、你的客户数据库。开发者 + DWS 的组合,在特定企业场景下,有能力在任务完成度上超越通用 Agent——同时成本更低、延迟更低、可控性更高。

这就是 Harness 工程的价值: 模型是商品化的,Harness 是差异化的。 谁能用最小的模型、最低的成本、完成最多的企业任务,谁就赢了。

面向未来的推演

短期(2026 H2):API 参数全面 Agent 友好化重设计。发布 Reference Implementation(至少 3 个行业场景)。提供 Agent 可消费的 Schema(OpenAPI 3.1 + JSON Schema + Examples)。

中期(2027):开发者社区围绕 Harness 六大组件形成生态。企业 Agent 任务完成度 benchmark 标准化。AutoResearch 工具链成熟——一键蒸馏任务专用小模型。

长期(2028+):开放平台从「连接层」进化为「企业 Agent OS」。Agent 像员工一样有工号、有绩效、有晋升。开发者成为「Agent 团队」的教练和管理者。


回到开头那个 OpenCode 的故事。

如果 DWS 的错误信息是自解释的(Missing required parameter: --title),如果三个 send 变体合并为一个命令用 --as 区分身份,如果 --to 带类型前缀让 Agent 不用猜 ID 种类——OpenCode 根本不需要我帮忙。它能自己写出正确的调用,自己处理重试和降级。我的工作只剩下 review 和部署。

对 Agent 友好,就是对开发者友好。只不过这次「友好」的标准变了——不是文档好读,而是 Schema 可推理;不是 SDK 好用,而是 Agent 能自主完成开发。

这,就是面向 AI 时代的开放平台新范式。


你在用 AI Coding 工具对接企业 API 时,遇到过哪些「Agent 看不懂」的参数设计?欢迎留言讨论。


See also