Agent 任务别只存日志:Checkpoint Replay 才是生产排障的底座

上周帮一个团队看 Agent 线上问题时,最典型的一句话又出现了:

"日志里能看到它调用了工具,但不知道为什么当时会选这个工具,也没法复现。"

这句话基本说明 Agent 已经从 demo 阶段进入了生产阶段。demo 阶段,日志能让你知道"程序有没有跑";生产阶段,日志只能回答"它大概发生过什么"。真正棘手的问题是:

  • 用户明明问 A,Agent 为什么检索了 B?
  • 工具返回里有一个边界值,为什么模型没有继续追问?
  • 第三步失败后,重试为什么把第一步的外部动作又执行了一遍?
  • 人工确认前后的状态是否一致?
  • 修完 prompt 或工具 schema 后,旧 badcase 能不能稳定复现?

如果这些问题都只能靠翻日志、猜上下文、手动拼请求,那系统迟早会变成"玄学服务"。我的判断很明确:Agent 生产化的分水岭,不是有没有 tracing,而是有没有 checkpoint replay。

这里的 checkpoint replay 不是一个很重的平台概念。你可以先把它理解成:在 Agent 执行的关键节点,记录足够完整、结构化、可校验的状态快照;当线上出现失败时,可以把某个 run 恢复到指定步骤,以"只读 / mock / 幂等保护"的方式重新执行,复现当时的决策链。

这篇文章不讲空泛理念,直接拆工程落地:哪些状态必须存,哪些东西不能重放,怎么用一个小型 Node.js 实现把日志升级成 checkpoint replay,以及团队如何按一周、一个月、一个季度逐步落地。

先把四个概念分清:trace、checkpoint、replay、store

很多团队说自己"有观测",实际只是把几段日志打到了平台里。Agent 场景里至少要区分四层东西:

概念 解决的问题 典型内容 常见误区
Trace 发生了什么 span、耗时、输入输出摘要、错误栈 以为 trace 等于可复现
Checkpoint 当时状态是什么 message、plan、tool result、变量、版本、游标 只存最终结果,不存中间状态
Replay 能否回到当时再走一次 从 checkpoint 恢复、mock 工具、对比输出 直接重放外部写操作
Store 跨会话长期记忆 用户偏好、知识、历史事实 把短期执行状态塞进长期记忆

LangGraph 的文档把 checkpointer 和 store 分得很清楚:checkpointer 持久化单个 thread 的图状态,适合会话连续性、人类介入、time travel、fault tolerance;store 则用于跨 thread 的长期数据。这个区分很重要,因为生产事故大多发生在"一个 run 内部的状态演化",不是长期记忆本身。

Trace 工具也很重要。LangSmith、Langfuse 这类平台能帮你看到调用链、模型输入输出、评估指标、prompt 版本等信息。问题是:**trace 更像录像,checkpoint replay 更像存档点。**录像能看,存档点能回去试。

Agent 的复杂性就在这里:一次执行里既有概率性模型调用,也有确定性工具调用,还有外部系统副作用。只看录像,你很难验证"如果我改了工具 schema,这个 badcase 会不会修好";有 checkpoint,你才能把修复变成回归测试。

为什么"只存日志"在 Agent 场景会失效

传统后端服务排障时,日志、指标、trace 已经覆盖了大多数问题。请求来了,代码路径相对确定,数据库状态可查,重试语义可控。但 Agent 服务多了三类不确定性。

第一类是上下文不确定性。模型看到的不是用户原始输入,而是一大坨动态拼装后的上下文:系统规则、历史消息、检索片段、工具列表、权限、预算、临时策略。日志里如果只写"用户问了什么"和"模型答了什么",中间最关键的 prompt assembly 过程就丢了。

第二类是决策不确定性。Agent 并不是每一步都由业务代码决定。它可能先规划、再调用工具、再根据结果修正计划。一次错误工具调用的原因可能不是工具本身,而是上一轮模型输出里隐藏的错误假设。

第三类是副作用不确定性。Agent 调用的工具越来越接近真实业务:发消息、建工单、改配置、查账单、触发工作流。失败后直接"再跑一遍"可能造成重复操作;不跑又无法复现。

所以只存日志会出现三个断点:

  1. 上下文断点:不知道模型当时完整看到了什么。
  2. 状态断点:不知道每一步执行后 Agent 内部变量如何变化。
  3. 副作用断点:不知道哪些外部动作已经真实发生,哪些只是准备发生。

Checkpoint replay 要补的就是这三个断点。

一个最小可用的 checkpoint 长什么样

我建议不要一上来追求"全量录制一切"。最小可用 checkpoint 只需要覆盖 7 类字段:

ts 复制代码
type AgentCheckpoint = {
  runId: string;
  stepIndex: number;
  threadId: string;
  nodeName: string;
  createdAt: string;

  versions: {
    agentVersion: string;
    promptVersion: string;
    toolSchemaVersion: string;
    modelRoute: string;
    policyVersion: string;
  };

  input: {
    userMessageHash: string;
    messages: Array<{ role: string; content: string }>;
    retrievedContext?: Array<{ id: string; source: string; textHash: string; text?: string }>;
  };

  state: {
    plan?: unknown;
    scratchpad?: unknown;
    variables: Record<string, unknown>;
    budget: { maxSteps: number; usedSteps: number; maxTokens?: number; usedTokens?: number };
  };

  toolCall?: {
    name: string;
    arguments: unknown;
    argumentsHash: string;
    idempotencyKey?: string;
    sideEffectLevel: 'none' | 'read' | 'write' | 'external';
  };

  toolResult?: {
    ok: boolean;
    resultHash: string;
    resultPreview?: unknown;
    errorType?: string;
    latencyMs?: number;
  };

  replay: {
    mode: 'live' | 'mock' | 'dry-run';
    canResumeFromHere: boolean;
    unsafeReasons?: string[];
  };
};

这里有几个字段容易被低估。

versions 必须存。Agent badcase 很多不是"模型突然变笨",而是 prompt、工具 schema、路由策略、权限策略有一个变了。没有版本,你无法判断一次 replay 的差异来自修复,还是来自环境漂移。

retrievedContext 最好既存 hash,也在合规允许时存文本快照。只存文档 ID 不够,因为知识库内容可能已经更新。你排查的是"当时为什么错",不是"现在看起来应该怎样"。

sideEffectLevel 是 replay 安全的核心。读操作可以 live replay;写操作默认只能 mock 或 dry-run;外部不可逆动作必须要求人工确认或幂等键保护。

canResumeFromHere 不要自动推断,应该由节点显式声明。有些节点天然可恢复,比如"根据已保存的工具结果继续总结";有些节点不应恢复,比如"已经发出外部付款请求但确认状态不明"。

可重放边界:别把 replay 做成事故放大器

Checkpoint replay 最大的坑,是把"复现"误做成"重新执行一遍"。Agent 系统里,重放必须按边界分层。

1. 模型调用:记录输入,允许替换输出

模型调用本身可以 replay,但要区分两种模式:

  • exact replay:使用当时保存的模型输出,不再调用模型,用于复盘状态机和工具链。
  • regression replay:用新的 prompt / schema / model route 再调用一次,用于验证修复效果。

两者都需要同一个 checkpoint,但目的不同。exact replay 问"当时系统为什么走到这里";regression replay 问"现在是否还会犯同样错误"。不要混在一起。

2. 工具读操作:可以 live,但要记录环境

例如搜索、数据库查询、读取工单详情。这类操作理论上可重放,但数据源会变化。因此 checkpoint 至少要保存 result hash 和必要 preview。排障时如果 live replay 的结果 hash 和当时不同,系统应提示"外部数据已漂移",而不是把差异算成模型修复。

3. 工具写操作:默认 mock,除非有幂等键

例如创建工单、发送通知、更新配置。这类操作不能直接重放。正确做法是:

  • 记录 idempotencyKey;
  • 记录请求 hash;
  • 记录外部系统返回的 envelope;
  • replay 时默认返回已保存的 toolResult;
  • 只有在沙箱环境或显式 dry-run endpoint 下才允许重新调用。

这和支付系统、消息系统的思路很像:外部副作用必须有幂等和状态查询能力,否则重放只会制造新事故。

4. 人工确认:确认本身也是状态

很多 Agent 系统有 human-in-the-loop,但只把它当 UI 弹窗。生产里,人工确认点应该进入 checkpoint:确认人、确认时间、确认前状态 hash、确认后的策略、允许执行的动作范围。

否则你会遇到一个很麻烦的问题:事故发生后,没人说得清"人当时到底确认了什么"。

参考实现:用 SQLite 做一个轻量 checkpoint recorder

下面给一个最小 Node.js 版本。它不依赖某个 Agent 框架,适合塞进现有服务里先跑起来。生产环境可以把 SQLite 换成 Postgres,结构不变。

ts 复制代码
import crypto from 'node:crypto';
import Database from 'better-sqlite3';

export type SideEffectLevel = 'none' | 'read' | 'write' | 'external';

export type CheckpointInput = {
  runId: string;
  threadId: string;
  stepIndex: number;
  nodeName: string;
  versions: Record<string, string>;
  input: unknown;
  state: unknown;
  toolCall?: {
    name: string;
    arguments: unknown;
    idempotencyKey?: string;
    sideEffectLevel: SideEffectLevel;
  };
  toolResult?: unknown;
  replay?: {
    canResumeFromHere: boolean;
    unsafeReasons?: string[];
  };
};

function sha256(value: unknown) {
  return crypto
    .createHash('sha256')
    .update(JSON.stringify(value))
    .digest('hex');
}

export class CheckpointStore {
  private db: Database.Database;

  constructor(file: string) {
    this.db = new Database(file);
    this.db.exec(`
      create table if not exists agent_checkpoints (
        run_id text not null,
        thread_id text not null,
        step_index integer not null,
        node_name text not null,
        created_at text not null,
        versions_json text not null,
        input_json text not null,
        state_json text not null,
        tool_call_json text,
        tool_result_json text,
        replay_json text not null,
        state_hash text not null,
        primary key (run_id, step_index)
      );

      create index if not exists idx_agent_checkpoints_thread
      on agent_checkpoints(thread_id, created_at);
    `);
  }

  save(cp: CheckpointInput) {
    const replay = cp.replay ?? { canResumeFromHere: true, unsafeReasons: [] };
    const stateHash = sha256({
      versions: cp.versions,
      input: cp.input,
      state: cp.state,
      toolCall: cp.toolCall,
      toolResult: cp.toolResult,
    });

    this.db.prepare(`
      insert into agent_checkpoints (
        run_id, thread_id, step_index, node_name, created_at,
        versions_json, input_json, state_json, tool_call_json,
        tool_result_json, replay_json, state_hash
      ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      cp.runId,
      cp.threadId,
      cp.stepIndex,
      cp.nodeName,
      new Date().toISOString(),
      JSON.stringify(cp.versions),
      JSON.stringify(cp.input),
      JSON.stringify(cp.state),
      cp.toolCall ? JSON.stringify(cp.toolCall) : null,
      cp.toolResult ? JSON.stringify(cp.toolResult) : null,
      JSON.stringify(replay),
      stateHash,
    );

    return { stateHash };
  }

  get(runId: string, stepIndex: number) {
    const row = this.db.prepare(`
      select * from agent_checkpoints
      where run_id = ? and step_index = ?
    `).get(runId, stepIndex) as any;

    if (!row) return null;

    return {
      runId: row.run_id,
      threadId: row.thread_id,
      stepIndex: row.step_index,
      nodeName: row.node_name,
      createdAt: row.created_at,
      versions: JSON.parse(row.versions_json),
      input: JSON.parse(row.input_json),
      state: JSON.parse(row.state_json),
      toolCall: row.tool_call_json ? JSON.parse(row.tool_call_json) : null,
      toolResult: row.tool_result_json ? JSON.parse(row.tool_result_json) : null,
      replay: JSON.parse(row.replay_json),
      stateHash: row.state_hash,
    };
  }
}

保存 checkpoint 的位置建议放在每个节点完成后,而不是整个 run 结束后。否则 Agent 中途崩溃时,你正好丢掉最需要的信息。

一个包装工具调用的例子:

ts 复制代码
async function runToolWithCheckpoint(params: {
  store: CheckpointStore;
  runId: string;
  threadId: string;
  stepIndex: number;
  toolName: string;
  args: unknown;
  sideEffectLevel: SideEffectLevel;
  execute: () => Promise<unknown>;
}) {
  const idempotencyKey = `${params.runId}:${params.stepIndex}:${params.toolName}`;

  params.store.save({
    runId: params.runId,
    threadId: params.threadId,
    stepIndex: params.stepIndex,
    nodeName: `before_tool:${params.toolName}`,
    versions: currentVersions(),
    input: { toolArgsHash: sha256(params.args) },
    state: currentAgentState(),
    toolCall: {
      name: params.toolName,
      arguments: params.args,
      idempotencyKey,
      sideEffectLevel: params.sideEffectLevel,
    },
    replay: {
      canResumeFromHere: params.sideEffectLevel === 'read',
      unsafeReasons: params.sideEffectLevel === 'read' ? [] : ['tool_has_side_effect'],
    },
  });

  const started = Date.now();
  try {
    const result = await params.execute();
    params.store.save({
      runId: params.runId,
      threadId: params.threadId,
      stepIndex: params.stepIndex + 0.1,
      nodeName: `after_tool:${params.toolName}`,
      versions: currentVersions(),
      input: { toolArgsHash: sha256(params.args) },
      state: currentAgentState(),
      toolCall: {
        name: params.toolName,
        arguments: params.args,
        idempotencyKey,
        sideEffectLevel: params.sideEffectLevel,
      },
      toolResult: {
        ok: true,
        resultHash: sha256(result),
        resultPreview: preview(result),
        latencyMs: Date.now() - started,
      },
      replay: { canResumeFromHere: true, unsafeReasons: [] },
    });
    return result;
  } catch (err: any) {
    params.store.save({
      runId: params.runId,
      threadId: params.threadId,
      stepIndex: params.stepIndex + 0.1,
      nodeName: `after_tool:${params.toolName}`,
      versions: currentVersions(),
      input: { toolArgsHash: sha256(params.args) },
      state: currentAgentState(),
      toolCall: {
        name: params.toolName,
        arguments: params.args,
        idempotencyKey,
        sideEffectLevel: params.sideEffectLevel,
      },
      toolResult: {
        ok: false,
        resultHash: sha256({ name: err.name, message: err.message }),
        errorType: err.name,
        latencyMs: Date.now() - started,
      },
      replay: { canResumeFromHere: true, unsafeReasons: [] },
    });
    throw err;
  }
}

上面代码里 currentVersions()currentAgentState()preview() 是你自己系统里的函数。重点不是这几个函数怎么写,而是记录的位置:工具调用前存意图,工具调用后存结果。

只存结果会丢掉"当时为什么要调用";只存意图会丢掉"外部世界到底返回了什么"。两边都存,排障才闭环。

Replay Runner:重放时默认不碰外部世界

有了 checkpoint,还需要一个 replay runner。最小规则是:

  • read 工具可以 live replay,但要标记数据漂移;
  • write / external 工具默认使用保存的 toolResult;
  • 模型调用默认 mock 成旧输出,除非显式进入 regression 模式;
  • replay 输出必须生成 diff,而不是只给"成功 / 失败"。

伪代码如下:

ts 复制代码
type ReplayMode = 'exact' | 'regression';

async function replayFrom(params: {
  store: CheckpointStore;
  runId: string;
  stepIndex: number;
  mode: ReplayMode;
}) {
  const cp = params.store.get(params.runId, params.stepIndex);
  if (!cp) throw new Error('checkpoint_not_found');

  if (!cp.replay.canResumeFromHere) {
    return {
      ok: false,
      reason: 'unsafe_checkpoint',
      unsafeReasons: cp.replay.unsafeReasons,
    };
  }

  const sandbox = createReplaySandbox({
    mode: params.mode,
    toolPolicy: (toolCall) => {
      if (toolCall.sideEffectLevel === 'read') return 'live-with-drift-check';
      return 'mock-saved-result';
    },
    modelPolicy: params.mode === 'exact' ? 'mock-saved-output' : 'call-current-route',
  });

  const replayResult = await sandbox.run({
    versions: cp.versions,
    input: cp.input,
    state: cp.state,
  });

  return {
    ok: true,
    checkpointHash: cp.stateHash,
    outputHash: sha256(replayResult.output),
    diff: compareReplay(cp, replayResult),
  };
}

工程上最有价值的是 diff。它至少应该回答:

  • prompt / policy / tool schema 版本是否变化;
  • 检索上下文 hash 是否变化;
  • 工具参数是否变化;
  • 工具结果是否变化;
  • 最终答案是否变化;
  • 变化是否命中预期修复点。

没有 diff 的 replay 只是在"重新跑一次"。有 diff,replay 才会变成回归测试。

一条真实排障链路应该怎么走

假设线上报警:Agent 自动处理工单时,把"需要人工确认"的退款请求归类成"可自动处理"。传统日志排障大概是:搜 runId、翻 prompt、翻工具结果、问业务同学、手动构造一个相似请求再跑一次。

Checkpoint replay 的流程应该是:

  1. 从报警拿到 runId 和失败 step。
  2. 打开 trace,看执行链路和耗时,确认异常节点。
  3. 定位该节点前后的 checkpoint。
  4. exact replay:用旧模型输出和旧工具结果恢复执行,验证状态机是否能复现错误路径。
  5. 查看 checkpoint diff:发现 policyVersion=refund-policy-2026-07-01,而工具 schema 里 requires_manual_approval 字段在该版本没有进入模型上下文。
  6. 修复 schema 映射或 prompt assembly。
  7. regression replay:使用新版本重新跑同一 checkpoint,确认 Agent 会进入人工确认分支。
  8. 把该 checkpoint 固化为 eval case,进入发布前回归集。

这个流程最大的收益不是"快一点",而是把事故从口头复盘变成结构化资产。每个线上坏例子都能沉淀成可重放样本,后面改 prompt、改工具、换模型路由时都能跑一遍。

Checkpoint Replay 和 Eval Harness 的关系

很多团队已经在做 eval harness:准备一批输入,看模型输出是否符合预期。这当然有用,但 Agent 的 badcase 往往不是单轮输入输出,而是中间状态出错。

离线 eval 更像"题库";checkpoint replay 更像"事故切片"。两者应该合并:

  • 从线上 checkpoint 中筛选高价值 badcase;
  • 清理敏感字段,生成可共享的 replay fixture;
  • 给 fixture 标注期望路径,而不只是期望最终答案;
  • 每次发布 prompt / schema / route 前跑 replay regression;
  • 对差异生成报告,要求 reviewer 看过再上线。

一个 fixture 可以长这样:

json 复制代码
{
  "case_id": "refund_manual_approval_001",
  "source_run_id": "run_20260713_0830_9fc2",
  "start_checkpoint": 4,
  "expected": {
    "must_call_tools": ["get_refund_policy"],
    "must_not_call_tools": ["execute_refund"],
    "final_state": "WAITING_HUMAN_APPROVAL"
  },
  "redactions": {
    "user_id": "hash",
    "order_id": "synthetic"
  }
}

注意这里的 expected 不是"回答里必须包含某句话",而是路径约束。Agent 工程里,路径经常比文案更重要。一个答案看起来没问题,但如果它绕过了审批工具,那就是事故。

什么时候只做 trace 就够,什么时候必须做 replay

不是所有 Agent 都需要一开始就上 replay。下面这张表可以作为判断:

场景 Trace 是否足够 是否需要 checkpoint replay 原因
纯问答助手,无外部工具 多数情况下够 可选 主要问题是回答质量
RAG 问答,有检索 不够 建议做 知识库会漂移,需要保存检索快照
工单分类 / 客服分流 不够 建议做 需要复现分类路径和策略版本
自动代码修改 不够 必须做 文件状态、工具调用、副作用复杂
自动运维 / 配置变更 远远不够 必须做 外部副作用高风险,必须可恢复
支付 / 退款 / 权限变更 远远不够 必须做且要人工 gate 不可逆动作,重放必须受控

我的建议是:只要 Agent 会调用业务工具,尤其是会产生写操作,就不要只停留在 trace。至少把 checkpoint 的数据结构先定下来,哪怕 replay runner 晚一点做。

最容易踩的 6 个坑

坑一:只存自然语言摘要。 摘要适合人看,不适合机器重放。checkpoint 必须存结构化字段,摘要只能作为附属信息。

坑二:不存版本。 没有 promptVersion、toolSchemaVersion、policyVersion,replay 差异基本不可解释。

坑三:把长期记忆当 checkpoint。 长期记忆会被更新、压缩、合并,不适合作为事故现场。checkpoint 要保留当时现场。

坑四:重放时真的执行写工具。 这是最危险的。写工具必须 mock、dry-run 或通过幂等键确认状态。

坑五:没有脱敏策略。 checkpoint 会保存上下文和工具结果,必须在采集层做字段级脱敏、加密和访问控制。否则观测系统会变成新的数据风险点。

坑六:只给平台团队用。 replay 的价值不只在基础设施团队。业务负责人、客服、测试、算法同学都应该能看到"这次错误路径是什么",只是权限视图不同。

团队落地路线:别一口吃成平台

如果你现在只有日志,不建议立刻做一个大平台。可以按三个阶段来。

第一周:把关键节点状态存下来

目标很简单:每个 run 有 runId,每个 step 有 checkpoint。先覆盖:

  • 用户输入 hash;
  • prompt / tool schema / policy 版本;
  • 工具调用参数和结果 hash;
  • 外部副作用级别;
  • 错误类型和耗时。

这一阶段不要求完整 replay,只要求事故发生后能看到结构化现场。

第一个月:做 exact replay 和 diff

第二阶段做一个内部 CLI 或页面:输入 runId 和 stepIndex,可以从 checkpoint 恢复,使用保存的模型输出和工具结果跑状态机,并生成 diff。

这个阶段重点是验证:你的状态机是否真的可恢复;哪些字段缺失会导致 replay 失败;哪些工具没有明确副作用级别。

第一个季度:接入回归与发布门禁

第三阶段把线上 badcase 变成 fixture,接入发布流程。每次改 prompt、工具 schema、模型路由、权限策略,都跑一遍 replay regression。

如果某个高风险 case 从 WAITING_HUMAN_APPROVAL 变成 AUTO_EXECUTE,发布直接阻断。这个门禁比"平均分提升 2%"更有生产意义。

一个实用的 checkpoint 分级标准

为了避免过度采集,可以把 checkpoint 分成三级:

等级 记录内容 适用场景 成本
L1 现场摘要 版本、输入输出 hash、错误、耗时 低风险问答、早期试点
L2 状态快照 messages、plan、变量、工具参数、结果 preview 大多数业务 Agent
L3 可重放现场 完整上下文、mock 结果、diff、fixture、脱敏副本 高风险写操作、核心业务

不要所有场景都上 L3。真正要做的是:按风险决定采集深度。一个内部知识库问答助手,L1/L2 可能够用;一个能改配置的运维 Agent,必须 L3。

对工程团队的真正价值

Checkpoint replay 听起来像"排障工具",但它最后会改变团队协作方式。

对开发来说,它把"我本地复现不了"变成"从第 5 步 checkpoint 开始 replay"。

对测试来说,它把线上事故变成可回归样本,而不是写在复盘文档里的故事。

对产品和业务来说,它能解释 Agent 为什么做出某个决策,而不是只给一个最终答案。

对管理者来说,它让 Agent 质量从"感觉还行"变成"关键路径 badcase 是否回归通过"。

这也是为什么我认为 checkpoint replay 会成为 Agent 工程的基础设施,而不是某个框架的高级功能。框架可以换,模型可以换,但"线上现场可恢复"这件事不会过时。

结论:Agent 生产化,要从"看见"走向"回到现场"

过去一年,很多团队补上了 tracing、prompt 管理、评估集,这些都很必要。但 Agent 真正进入生产后,最值钱的问题会变成:

事故发生时,我们能不能回到那个现场?

如果不能,你只能靠日志猜。能回到现场,你就可以复盘、修复、回归、阻断发布。

所以我的建议很直接:只要你的 Agent 已经接入业务工具,从今天开始给它加 checkpoint。先不用复杂,先把版本、状态、工具意图、工具结果、副作用级别存下来。等下次线上 badcase 出现时,你会庆幸自己不是只留了一堆"模型调用成功"的日志。

日志告诉你它曾经来过。Checkpoint replay 让你回到它出错的那一刻。

相关推荐
怕浪猫3 小时前
第13章 评估与评测:如何衡量Agent的好坏
langchain·openai·ai编程
VIP_CQCRE2 天前
用 Ace Data Cloud 快速接入 OpenAI Chat Completion API:从基础调用到多模态应用
ai·chatgpt·openai·api·acedatacloud
小Bk2 天前
我用 Next.js 16 + DeepSeek API 做了一个 AI 简历吐槽器,已开源
openai·next.js·deepseek
TrisighT2 天前
Agent 的工具调用我从来不用官方推荐那套写法——4 个我实测下来更稳的隐藏用法
aigc·openai·cursor
AINative软件工程2 天前
别只会重试:LLM JSON 失败 5 类修复法
openai
仙逆GPT2 天前
GPT-5.6在ChatGPT中如何显示?模型入口与推理档位说明
chatgpt·openai·sol·gpt-5.6
武子康2 天前
🔥 GPT-5.6 有限预览全拆解:Sol/Terra/Luna 三档定价 + max/ultra 双推理 + 缓存 1.00×/1.25×/0.10× 三倍
人工智能·chatgpt·openai
武子康2 天前
OpenAI Workspace Agents 进入信用计费:Coding Agent 从额度体验走向成本治理
人工智能·openai·claude
云卷云舒___________2 天前
OpenAI 发布 ChatGPT Work,Codex 并入 ChatGPT,Kimi Code 推出 K2.7 高速模式 | 7月10日 AI日报
chatgpt·openai·codex·ai日报·chatgptwork·kimicode·k27