# Data Edit

> 自然语言编辑、修改、更新、变更或调整 CRM 主对象已有记录的字段值；禁止修改从对象、子对象、明细行或 details。用于修改客户信息、变更联系人电话、调整商机主对象金额、编辑主对象字段等业务对象，或用户表达 帮我改一下/把XX改成/更新一下/修改这条记录/调整字段值/编辑一下XX 等意图。线索状态流转或线索专属动作不走本 skill，包括把线索改为跟进中、将线索修改为无效/作废/关闭、记录线索跟进结果、设置下次跟进时间等，应优先走线索专属 skill 或专属能力。

- Skill: `ahang1598/data-edit` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add ahang1598/data-edit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ahang1598/data-edit/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ahang1598 (https://skillmd.com/u/ahang1598)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/ahang1598/data-edit

---

# data-edit

把用户的自然语言变成一次安全的 CRM 主对象记录更新。整条链路是一个串行的解析过程：

```
准备编辑表单 → 组装 object_data → 保存 → 返回结果
```

每一步都依赖上一步的真实返回值。这条 skill 的核心风险只有一个：**在信息还没确定时就往下猜**——猜错对象、改错记录、给只读字段回填值、编造关联记录 ID、把展示名当提交值写进枚举。编辑比新建更危险，因为它直接覆盖一条已经存在、用户正在用的真实数据，改错往往要事后才被发现。所以下面每一步的约束，本质都是在回答同一个问题：*“我现在掌握的信息，足够让我安全地改下一步吗？”* 信息不足时，宁可停下来问用户，也不要编造。

一个贯穿全程的原则：**用户中途改了任何会影响结果的信息（对象、目标记录、字段值、关联记录），就从受影响的最早步骤重新走一遍，不要复用旧的表单上下文。** 那些旧数据是针对旧输入算出来的，复用就等于拿过期的真相去覆盖一条真实记录。

## 不适用场景 / 路由排除

本 skill 只处理通用主对象字段编辑。**进入流程前，核查当前业务对象是否存在与用户意图完全匹配的专属能力；有则直接走专属能力，不启用本能力。**

### 专属操作路由表

以下操作有专属能力，不走本 skill：

| 操作 | 路由原则 |
|---|---|
| 客户/线索分配 | 走对应专属能力 |
| 客户/线索退回 | 走对应专属能力 |
| 客户/线索收回 | 走对应专属能力 |
| 客户/线索转移 | 走对应专属能力 |
| 客户/线索移除 | 走对应专属能力 |
| 客户/线索领取 | 走对应专属能力 |
| 线索无效 | 走线索专属能力 |
| 线索跟进 | 走线索专属能力 |
| 线索转换 | 走线索专属 skill |

### 从对象排除

本 skill 只改主对象字段。**用户要求修改从对象、子对象、明细行或 `details` 时，停下来说明本能力只支持主对象字段编辑。**

## 工作流总览

| 步骤 | 目的 | 走下一步的前提 |
|---|---|---|
| 0 路由核查 | 确认没有更合适的专属能力 | 当前请求属于主对象通用字段编辑 |
| 1 准备编辑表单 | 一步完成识别对象、定位唯一记录、获取编辑表单 | 已拿到唯一 `object_api_name`、唯一 `data_id` 和 `form` |
| 2 组装 object_data | 在原记录基础上构造修改后快照 | 命中对象有专属引用文件时已读；关联字段已解析到唯一 ID |
| 3 保存 | 写入系统 | object_data 已组装完成 |
| 4 返回结果 | 回传更新卡片 | — |

## 步骤 1：准备编辑表单

**先拿到记录关键词和表单，再做任何其他事。** 在拿到编辑表单前，不要去解析关联记录、不要查任何关联 ID、也不要凭空向用户罗列“能改哪些字段”。

这一步只需要两样输入：**对象名** 和 **记录关键词**。对象识别只接受对象类型名（如“客户 / 商机 / 联系人”）；要定位的那条记录的名称、编号、标题或记录 ID 用来定位具体记录。不要把记录关键词或要改的字段值当成对象名。

| 用户输入 | 对象识别使用 | 记录定位使用 |
|---|---|---|
| 把客户大麦网的来源改成线上注册 | `客户` | `大麦网` |
| 修改联系人张三的电话 | `联系人` | `张三` |

- 缺记录关键词时，先问用户“要编辑哪一条记录”
- 一句话里识别不出明确对象类型词时，先问用户“要编辑哪一类对象”
- 用户要改的字段和新值留到步骤 2 再用

### 1.1 识别对象

对象 apiName 未明确时，调用对象识别工具：

```json
{
  "apiName": "IdentifyObjectWithDescribe",
  "query": "<对象名称>",
  "include_simple_describe": false
}
```

处理规则：

- `resolution_status = RESOLVED` 且仅 1 个候选：采用该 `object_api_name`
- `resolution_status = AMBIGUOUS`：把 `object_candidates` 交给用户确认
- `resolution_status = NO_MATCH` 或无可用候选：请用户更换对象名称或确认该对象是否存在

对象 apiName 已明确时，直接跳过对象识别。

### 1.2 定位唯一记录

记录 ID 未明确时，调用按名称查询记录 ID 工具：

```json
{
  "apiName": "QueryRecordIdByName",
  "query": "<记录关键词>",
  "apiNames": ["<object_api_name>"]
}
```

处理规则：

- 命中 1 条：采用该记录 `id`
- 命中多条：把候选交给用户确认；确认前不要继续
- 命中 0 条：请用户提供更准确的名称或编号

需要查看候选记录的更多字段来帮助用户判断时，再调用：

```json
{
  "apiName": "QueryRecordByName",
  "name": "<记录关键词>",
  "object_api_names": ["<object_api_name>"]
}
```

记录 ID 已明确时，直接跳过记录定位。

### 1.3 获取编辑表单

调用编辑表单工具：

```json
{
  "apiName": "getEditFormContext",
  "object_api_name": "<object_api_name>",
  "data_id": "<data_id>"
}
```

返回中的以下三部分是后续组装数据的唯一真相：

- `form_fields`
- `field_metadata`
- `object_data`

**同一对象 + 同一条记录、且尚未保存过，可复用上一份表单。** 如果本轮对话里刚刚已经为某条记录拿到编辑表单，且还没对这条记录成功保存过，就直接复用上一次返回的 `form`。

关于业务类型：编辑沿用记录已有的 `record_type`，不主动选择。如 `object_data` 已含 `record_type`，原样保留；除非字段元数据和对象专属规则都明确允许、且用户明确要求，否则不新增、删除或改写它。

## 步骤 2：组装 object_data

以 `form.object_data` 为基础**复制一份**，再合并用户要求修改的字段。只认步骤 1 返回的这份表单，几条铁律：

1. **保留未改动的一切。** 保留 `_id` 或数据主键，保留用户没要求改的原值。
2. **只读字段是雷区。** `form_fields` 中 `is_readonly=true` 的字段保留原值，绝不写入用户值、推断值或计算值。
3. **必填以 `is_required` 为准。** 不删除已有必填字段值，除非用户明确要求清空且该字段允许为空。
4. **选项字段只认提交值。** `field_metadata` 里带 `options` 的字段，必须写合法 value 而非展示名；映射不唯一或找不到时先确认。
5. **不碰从对象。** 不构造、不修改、不提交 `details`，不修改从对象、子对象或明细行。
6. 除接口或对象专属规则明确要求外，只写 `form_fields` 中存在且非只读的字段。

### 2.1 对象专属逻辑（命中即先读）

有些对象有超出通用规则的特殊构造逻辑。组装前先按下表判断是否需要读引用文件：

| 目标对象 `apiName` | 必须先读的引用文件 |
|---|---|
| `ActiveRecordObj` | `{Current agent directory}/skill/{skillApiName}/references/ActiveRecordObj.md` |

命中时先读对应文件并应用其约束，再构造 `object_data`。引用文件里的特殊构造逻辑优先于通用规则；但可改字段、必填状态、枚举提交值仍以本次编辑表单返回为准。

### 2.2 关联对象字段（必须解析到唯一 ID）

关联记录的解析只在这一步做，前提是表单里确实存在这个关联字段。先在 `form_fields` / `field_metadata` 里确认该字段存在，并拿到真实 `field_name` 和关联对象 `apiName`。

当字段类型是 `object_reference`、`master_detail`、`object_reference_many`，或元数据表明该字段要关联已有记录时，必须先解析成唯一可提交的记录 ID 再写入。

解析路径：

- 用户直接给了符合字段要求的明确 ID：可直接用
- 用户给的是名称 / 编号 / 手机号 / 公司名 / 联系人名等文本：从该字段 `field_metadata` 中读取关联对象 apiName，再调用按名称查询记录 ID 工具

```json
{
  "apiName": "QueryRecordIdByName",
  "query": "<关键词>",
  "apiNames": ["<关联对象apiName>"]
}
```

需要查看候选详情再让用户判断时，再调用：

```json
{
  "apiName": "QueryRecordByName",
  "name": "<关键词>",
  "object_api_names": ["<关联对象apiName>"]
}
```

处理规则：

- 查到 1 条：可用
- 查到多条：交给用户选择
- 查不到 / 无法判断：停下来，不要编造 ID 或默认选第一条

### 2.3 各字段类型的写法

| 字段类型 | 写法 | 示例 |
|---|---|---|
| `owner` / `employee` / `department` / `dimension` | 按元数据要求用 ID 数组 | `{"owner": ["1016"]}` |
| `date` | 日期字符串 `yyyy-MM-dd` | `"close_date": "2026-06-08"` |
| `date_time` | 日期时间字符串 `yyyy-MM-dd HH:mm:ss` | `"meeting_time": "2026-06-08 14:30:00"` |
| `select_one` | 元数据中的合法提交值 | `"sales_stage": "1"` |
| `select_many` | 数组，元素为合法提交值 | `"crm__c": ["Salesforce"]` |
| `percentile` | 提交值是百分号前的数值（可小数），不要换算成 0–1 比例 | `"discount_rate": 90` |
| `rich_text` | 使用纯文本内容，不要写入 HTML 标签或富文本源码 | `"description": "客户希望下周二沟通方案"` |
| `object_reference` / `master_detail` | 唯一命中或用户确认后的记录 ID 字符串 | `"account_id": "69e9a174..."` |
| `object_reference_many` | 唯一命中或用户确认后的记录 ID 数组 | `"contact_ids": ["69e9a174..."]` |

## 步骤 3：保存数据

object_data 组装完成后，直接调用更新工具。保存 payload 中不得包含 `details`。

```json
{
  "apiName": "UpdateRecordsByData",
  "object_api_name": "<object_api_name>",
  "object_data": {
    "_id": "<data_id>",
    "...": "..."
  }
}
```

仅在确有需要时，才传入 `missing_required_field_handling`；默认沿用后端校验结果，不自行猜测补值。

## 步骤 4：返回结果

调用保存后，先判断接口返回，只有记录确实更新成功时才视为成功。

- 需要二次确认：把系统要求确认的内容如实转达给用户，等用户确认后再重新提交
- 用户明确拒绝本次更新：立即终止整个操作
- 权限 / 禁止执行 / 当前对象不支持：立即终止，不重试
- 保存失败 / 报错：把失败原因如实告诉用户；仅当错误明确指出某个可修正字段问题时，修正后至多重试一次

