给 Agent 准备数据:别只喂文档,要设计可行动的上下文

2026-07-09 37 预计阅读时间: 1 分钟
来源: huggingface.co AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

Agent 应用和传统搜索、问答系统的差别,不只是“多了一个大模型”。Agent 会规划、调用工具、写入状态、根据反馈重试。也就是说,数据不再只是被检索出来给人看,而是会被模型拿来做决策。因此,“Data for Agents”这个主题的关键,不是把所有资料塞进向量库,而是把数据整理成 Agent 能理解、能验证、能执行的上下文。

由于来源摘要没有提供更细的实现细节,下面的实践示例按一个常见假设展开:你正在构建一个能回答内部知识、并可调用工具的 Agent。示例不是原文事实,而是可以这样实践的一套最小方案。

Agent 需要的数据,不等于普通 RAG 需要的数据

普通 RAG 系统通常关心三件事:文档切片、向量检索、把片段放进提示词。Agent 当然也需要这些,但还不够。

Agent 至少还会消耗四类数据:

  • 知识数据:产品文档、接口说明、操作手册、FAQ、工单记录。
  • 工具描述:有哪些工具、参数是什么、失败时返回什么、哪些操作有副作用。
  • 运行状态:当前任务、已尝试步骤、用户偏好、会话内事实。
  • 可验证信号:数据库记录、API 返回、日志、权限信息、测试结果。

问题往往出在边界上。比如文档里写“重启服务后等待健康检查通过”,人能理解流程,但 Agent 需要知道:重启哪个服务、用哪个命令、健康检查端点是什么、超时时间多少、失败后是否允许回滚。

因此,给 Agent 准备数据时,要从“给模型看”转向“给模型用”。文档片段应该尽量带结构化元数据,工具说明应该明确副作用,状态数据应该能被更新和审计。

数据切片要保留动作边界

很多团队切文档时只按固定 token 数切,比如每 800 token 一段。这对普通问答尚可,但对 Agent 不稳定。因为 Agent 需要执行动作,如果一个操作流程被切成两半,模型可能拿到“怎么检查”却没拿到“什么时候检查”,或者拿到命令却没拿到前置条件。

更适合 Agent 的切片方式是按语义和动作边界切:

  • 一个 API 端点尽量独立成块。
  • 一个运维 Runbook 的一个步骤或一个完整故障场景独立成块。
  • 每个块保留标题、来源类型、更新时间、权限级别、适用系统。
  • 对“危险操作”打标签,例如 destructive: truerequires_approval: true

这样做的好处是,Agent 检索到的不只是文本,而是一块有上下文、有约束、有风险标记的数据。

可以这样实践:把文档整理成 Agent 可检索的 JSONL

下面是一个最小可运行的 Python 示例:读取 Markdown 文档,按标题切块,并输出带元数据的 JSONL。实际项目里你可以把结果写入向量库、搜索引擎或对象存储。

运行前准备:把 docs/ 改成你的文档目录,或先用示例命令创建测试文档。

mkdir -p docs
cat > docs/restart-api.md <<'EOF'
# Restart API Service

## Preconditions
User must have production operator permission. Check current incident status before restart.

## Restart command
Run `kubectl rollout restart deployment/api -n prod`.

## Health check
Call `https://api.example.com/healthz` and expect HTTP 200 within 120 seconds.

## Rollback
If health check fails, rollback the deployment and page the owner.
EOF

创建 prepare_agent_data.py

from pathlib import Path
import hashlib
import json
import re

DOC_DIR = Path("docs")
OUT_FILE = Path("agent_chunks.jsonl")

heading_re = re.compile(r"^(#{1,3})\s+(.+)$", re.MULTILINE)


def stable_id(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]


def infer_risk(text: str) -> dict:
    lower = text.lower()
    destructive_words = ["delete", "drop", "rollback", "restart", "shutdown"]
    requires_approval = any(word in lower for word in destructive_words)
    return {
        "requires_approval": requires_approval,
        "risk": "high" if requires_approval else "normal",
    }


def split_markdown(path: Path):
    text = path.read_text(encoding="utf-8")
    matches = list(heading_re.finditer(text))

    if not matches:
        yield {
            "id": stable_id(str(path) + text),
            "source": str(path),
            "title": path.stem,
            "content": text.strip(),
            **infer_risk(text),
        }
        return

    for i, match in enumerate(matches):
        start = match.start()
        end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
        title = match.group(2).strip()
        content = text[start:end].strip()
        if not content:
            continue
        yield {
            "id": stable_id(str(path) + title + content),
            "source": str(path),
            "title": title,
            "content": content,
            "content_type": "runbook" if "restart" in str(path).lower() else "doc",
            **infer_risk(content),
        }


def main():
    chunks = []
    for path in sorted(DOC_DIR.glob("**/*.md")):
        chunks.extend(split_markdown(path))

    with OUT_FILE.open("w", encoding="utf-8") as f:
        for chunk in chunks:
            f.write(json.dumps(chunk, ensure_ascii=False) + "\n")

    print(f"wrote {len(chunks)} chunks to {OUT_FILE}")


if __name__ == "__main__":
    main()

运行:

python prepare_agent_data.py
head -n 3 agent_chunks.jsonl

你会得到类似这样的数据块:

{"id":"...","source":"docs/restart-api.md","title":"Restart command","content":"## Restart command\nRun `kubectl rollout restart deployment/api -n prod`.","content_type":"runbook","requires_approval":true,"risk":"high"}

这类 JSONL 的价值在于,它能同时服务检索和控制逻辑。检索阶段可以用 content 做 embedding;执行阶段可以读取 requires_approval,要求用户确认后再调用有副作用的工具。

工具数据要写清楚失败模式

Agent 的工具描述经常被写得像 SDK 注释:函数名、参数、返回值。真正上线后,更重要的是失败模式和权限边界。

例如一个重启服务工具,描述里应该包含:

  • 是否会影响生产流量。
  • 哪些 namespace 允许操作。
  • 是否需要人工确认。
  • 失败返回是否可重试。
  • 执行后应该检查哪个信号。

可以这样写一个工具注册配置:

tools:
  - name: restart_kubernetes_deployment
    description: Restart a Kubernetes deployment in an approved namespace.
    side_effect: true
    requires_approval: true
    allowed_namespaces:
      - staging
      - prod
    input_schema:
      type: object
      required: [namespace, deployment]
      properties:
        namespace:
          type: string
          enum: [staging, prod]
        deployment:
          type: string
          pattern: "^[a-z0-9-]+$"
    success_check:
      type: http
      timeout_seconds: 120
      expected_status: 200
    retry_policy:
      max_attempts: 1

这不是某个框架的固定格式,而是一种值得采用的数据形态:把“能不能做、怎么做、做完如何验证”写成机器可读结构。Agent 的提示词可以引用它,工具执行层也可以用它做硬校验。

不要把记忆当成垃圾桶

Agent 往往会引入 memory:用户偏好、历史任务、长期事实、阶段性计划。这里最大的风险是污染。一次错误判断如果被写成长久记忆,后续任务会持续受影响。

更稳妥的做法是给记忆分层:

  • 会话记忆:只在当前对话有效,例如“用户刚才选择了 staging”。
  • 任务记忆:只在当前工单或当前工作流有效,例如“已经重启过 api deployment”。
  • 长期记忆:必须经过确认或规则过滤,例如“该团队默认使用 prod namespace”。

写入长期记忆前,最好有验证步骤。比如只允许从明确用户声明、组织配置、成功执行结果里提取;不要从模型推测里提取。

落地检查清单

准备 Agent 数据时,可以用下面这份清单压一遍设计:

  • 数据块是否保留了标题、来源、更新时间和适用范围?
  • Runbook 是否按完整动作或故障场景切片,而不是机械 token 切片?
  • 工具描述是否标注副作用、权限、失败模式和验证方式?
  • 高风险数据是否带有 requires_approval 一类的机器可读字段?
  • Agent 的运行状态是否可审计、可过期、可删除?
  • 长期记忆是否有写入门槛,而不是把每轮对话都沉淀下来?

Agent 的能力上限,很大一部分取决于数据设计的下限。把数据整理成可检索、可执行、可验证的形态,模型才不容易在漂亮的文本里迷路,也更容易接入真实系统。


相关推荐