用户让它"把昨天的调研整理成文章,存草稿,加入专题,发布出去"。

它搜了资料,写了初稿,保存了草稿,然后在"加入专题"这一步,模型调用超时了。

你觉得它该从断点接着跑,还是从头再来一遍?

如果从头再来——它会再存一篇草稿,再扣一次费,可能再发一次文。用户第二天看到的,是草稿箱里躺着两篇一模一样的文章,专栏里挂着一条重复的发布记录。

这个场景不是虚构的。只要一个 AI 系统拥有"调用工具、改数据"的能力,它就不再只是生成文字,而是在运行一个程序。而程序,就有断电、超时、重复执行、状态不一致这些毛病。

所以生产级的 Agent 从来不是"更聪明的提示词 + 更多工具"。它是一套受约束、可追踪、可暂停、可恢复的执行系统

一句话概括它和普通聊天的区别:

LLM 负责提出判断和计划,真正的系统负责控制它"能看到什么、能做什么、失败后怎么办"。

下面这篇文章,就是用一张总领链路图开头,一节一节把这条链路拆开讲。每一节都有一张局部的链路设计,最后再拼回一张完整的图。


先看整条链路(总领)

不要急着看代码,先记住这张图。这篇文章的所有章节,都是这张图里的某一段。

用户目标
   │
   ▼
AIApplicationService        ← 编排一次"任务",不是拼接一次 Prompt
   │
   ▼
ContextBuilder              ← Context Compiler:身份 → 检索 → 去重 → 排序 → 过滤 → Token 预算
   │
   ▼
LLMGateway                  ← 模型选择 / 重试 / 超时 / 计费;只返回"建议",不碰真实系统
   │  工具调用请求
   ▼
ToolGateway ──► PolicyEngine ← 权限 / 风险分级 / 审批 / Schema / 限额
   │
   ▼
UseCase → Domain → DB       ← 真正执行业务;返回 Receipt
   │
   ▼
AgentRun                    ← 每步落 Checkpoint,断了续跑,问"为什么"可 Replay

这张图里最关键的一件事:模型只占中间小小一格。 判断权归它,但执行权不在它手上。它所有的"想法"都以请求的形式发出去,由外面这一圈确定性的系统来裁决和落地。

链路的最外层,还有一条不画进来的回路——Promotion:线上执行的 Trace 沉淀成候选案例,经过离线评估和人工审核后,才被晋升为新的策略。这条回路是让系统"越用越准"但又不至于自己乱改行为的钥匙,后面第七章专门讲。


一、Agent Runtime:模型的"大脑",系统的"操作系统"

先建立一个直觉。

普通 AI 对话是条单向流水线:

用户问题
   │
   ▼
拼接 Prompt
   │
   ▼
调用模型
   │
   ▼
返回文本

Agent 则是多跳的:

用户目标
   │
   ▼
模型分析
   │
   ▼
搜索知识库 → 读取文件 → 创建笔记 → 修改数据 → 返回结果

差别不在"多调了几次模型",而在于:第二步开始,系统在真实世界里留下了痕迹。 创建了笔记、改了数据,这些动作是不可撤销的、有副作用的、要花钱的。

于是需要一个 Agent Runtime,专门管下面这些事:

  • 给模型哪些上下文
  • 允许调用哪些工具
  • 工具参数是否合法
  • 是否需要用户审批
  • 调用失败是否重试
  • 如何记录整个执行过程
  • 中断后如何继续

它和操作系统的职责一模一样:你不是给每个进程发一把内核钥匙,而是让它通过系统调用去申请资源,由内核裁决。所以:

LLM = 大脑,负责推理
Agent Runtime = 操作系统,负责约束和执行

边界要怎么立

落实到代码,第一步是划出四条边界,任何业务代码都不得越过去:

public interface AgentRuntime {
    AgentRun start(AgentCommand command);   // 开启一次任务,先落运行记录
    AgentRun resume(String runId);          // 断电/超时后,从断点续跑
    AgentTrace trace(String runId);         // 拿回放凭证,回答"为什么"
}
public class AIApplicationService {
    private final ContextBuilder contextBuilder;
    private final LLMGateway llmGateway;
    private final ToolGateway toolGateway;
    private final AgentRunStore runStore;

    public AgentResult handle(AgentCommand cmd) {
        // 1. 先建运行记录 —— 任务是"可以放回放的",不是一次性的
        AgentRun run = runStore.create(cmd);

        // 2. 编译上下文(第二节)
        CompiledContext ctx = contextBuilder.compile(cmd.asRequest());

        // 3. 让模型"建议"下一步 —— 它只表达意图
        LLMResponse resp = llmGateway.complete(ctx, run);

        // 4. 建议不直接执行,交给 ToolGateway 走策略门卫(第三节)
        List<ToolReceipt> receipts = resp.proposedCalls().stream()
                .map(toolGateway::execute)
                .toList();

        run.markCompleted(receipts);
        return AgentResult.of(resp.answer(), receipts);
    }
}

这条链路里,AIApplicationService 是唯一入口。它不允许出现的事是——某个业务方法里直接 new OpenAIClient()、直接 noteRepository.delete()、直接绕过权限系统。

这条链路的形状

Controller
   │
   ▼
AIApplicationService
   │
   ▼
ContextBuilder → LLMGateway → 模型供应商
   │
   └──► ToolGateway → UseCase → Domain

二、Context 是编译出来的,不是临时拼接的

很多初期系统是这么拼 Prompt 的:

系统提示词
+ 用户问题
+ 最近聊天记录
+ 搜索到的笔记
+ 用户资料
= 最终 Prompt

看着能跑,跑一段时间问题全冒出来:

  • 上下文越来越长,不知道哪些内容真正重要
  • 私密内容可能错误地进了 Prompt
  • 旧笔记和新笔记互相冲突,模型不知道该信谁
  • 摘要模块查一遍笔记,问答模块又查一遍,各拼各的
  • Token 成本失控,没人说得清一次调用花了多少
  • 出错了,谁也不知道模型当时到底看到了什么

问题不在"拼接"这个动作,而在没有一层东西统一负责"拼"。所以就有了 Context Compiler——一个专门的上下文构建层。

它和编译器的思路是同一套:

原始资料 → 检查、转换、优化 → 可执行输入

编译管道的链路

用户请求
   │
   ▼
Context Compiler
   ├─ 身份和权限检查      ← 先确认"能看到什么",再决定"要查什么"
   ├─ 任务类型判断        ← 这次是问答、摘要还是写作,指令模板不同
   ├─ 知识检索            ← 统一走 ContextService,各模块不许自己查
   ├─ 内容去重            ← 同一篇文档出现在多个来源,只留一份
   ├─ 相关度排序
   ├─ 新鲜度判断          ← 旧笔记降权,冲突时新版本优先
   ├─ 敏感信息过滤        ← 该脱敏的脱敏,该剔除的剔除
   ├─ Token 预算分配      ← 预算不够先砍证据,不砍权限
   └─ 上下文压缩
   │
   ▼
结构化 Context
   │
   ▼
LLM

这里最值得记住的设计原则是:stage 的顺序即策略。 身份检查永远在最前面——你不能在不知道"这人是谁"之前就去检索;Token 预算永远在靠后的位置——先保证内容对,再压缩体积。

代码怎么写

不要给每个 AI 功能发一份自己的查笔记逻辑:

AI 摘要模块自己查笔记   ✗
AI 问答模块自己查笔记   ✗
AI 写作模块自己查笔记   ✗
Agent 自己查笔记        ✗

统一收敛到 ContextBuilder 这一个管道。

public record ContextRequest(
        String userId,
        String workspaceId,
        TaskType taskType,         // SUMMARIZE / QA / WRITING / AGENT
        String currentNoteId,
        String query,
        int tokenBudget
) {}
public record CompiledContext(
        String instructions,                 // 按任务类型渲染的系统指令
        UserContext userContext,             // 身份、偏好、可见范围
        ConversationContext conversation,    // 最近的对话
        List<Evidence> knowledgeEvidence,    // 检索证据,每一条都带来源和时间
        List<String> citations,              // 引用清单,供回执和审计
        PermissionScope permissions,         // 这个上下文允许触碰的边界
        String contextVersion                // 快照版本,Replay 时靠它还原现场
) {}
public interface ContextStage {
    void apply(ContextRequest request, CompiledContext.Builder builder);
}
public class ContextBuilder {
    private final List<ContextStage> stages;

    public CompiledContext compile(ContextRequest request) {
        var builder = CompiledContext.builder();
        stages.forEach(stage -> stage.apply(request, builder));
        return builder.build();
    }
}
// 注册顺序就是编译顺序,谁先谁后是设计,不是巧合
new ContextBuilder(List.of(
        new IdentityStage(),          // 你是谁、能看什么 —— 永远第一位
        new TaskRouterStage(),        // 这次是问答还是写作
        new RetrievalStage(),         // 统一查知识库
        new DedupStage(),             // 重复内容只留一份
        new RelevanceStage(),         // 按相关度排序
        new FreshnessStage(),         // 旧内容降权
        new SensitiveFilterStage(),   // 该脱敏的脱敏
        new TokenBudgetStage(),       // 预算不够先砍证据
        new CompressStage()           // 最后压进窗口
));

这样做的收益是:模型怎么换、RAG 怎么改、压缩算法怎么升级,都不需要动业务代码——它们全被挡在 ContextBuilder 这一层后面。


三、Agent 可以申请执行,但不能自己决定有没有权限

先看一个错误设计:

Agent
   │
   ▼
DeleteNoteTool
   │
   ▼
删除笔记

然后在 Prompt 里写一句"没有用户同意,不要删除笔记"。

Prompt 只是软约束,不是安全机制。 模型可能误判用户的意图,可能被注入的提示词误导,也可能就是状态不对产生幻觉。你把安全建立在"模型每次都记得这句叮嘱"上,等于把门锁建在"每个人都自觉关门"上。

生产级的做法是加一道硬门卫:

Agent
   │  提出工具调用请求
   ▼
Tool Gateway
   │
   ▼
Policy Engine
   ├─ 当前用户是谁
   ├─ 是否拥有权限
   ├─ 操作是否高风险
   ├─ 是否需要审批
   ├─ 参数是否符合 Schema
   └─ 是否超过调用限额
   │
   ▼
真正执行工具

核心思想就一句:

Agent 可以申请执行,但不能自己决定自己有没有权限。

就像普通用户不能因为说"我是管理员",系统就允许他删数据。判断权限的地方是 PolicyEngine,不是模型。

代码怎么写

public class ToolGateway {
    private final PolicyEngine policy;
    private final ToolRegistry tools;

    public ToolReceipt execute(AgentId agentId, ToolInvocation call) {
        PolicyDecision decision = policy.decide(agentId, call);

        if (decision.blocked()) {
            if (decision.requiresApproval()) {
                throw new ApprovalRequired(decision.approvalId());
            }
            throw new AccessDenied(decision.reason());
        }

        // 只有策略放行了,才真正执行
        return tools.get(call.toolName()).execute(call.payload());
    }
}
public record PolicyDecision(
        boolean allowed,
        RiskLevel risk,
        String approvalId,      // 需要审批时,返回凭证给上游
        String reason
) {}

策略判定的基础是风险分级。把工具按危害面分三档:

public enum RiskLevel {
    LOW,      // search_notes / read_note / list_topics / get_related_notes
    MEDIUM,   // create_draft / update_note / add_tag / move_note
    HIGH      // delete_note / publish_content / bulk_update / change_permissions / export_private_data
}

低风险直接放行,中风险要校验权限和限额,高风险必须走审批——而且要落到代码上,不是靠提示词:

public class ApprovalService {
    // 申请:高风险操作先领一张审批凭证,此时还不执行
    public ApprovalTicket request(AgentId agentId, ToolInvocation call) {
        return new ApprovalTicket(UUID.randomUUID().toString(), agentId, call);
    }

    // 确认:同一张凭证只能消费一次(CAS),防重复确认
    public ApprovalResult confirm(String ticketId, String userId) {
        ApprovalTicket ticket = tickets.claim(ticketId, userId);
        return new ApprovalResult(ticket, Instant.now());
    }
}

高风险操作的链路是:

Agent 申请执行
   │
   ▼
Gateway 拦截
   │
   ▼
Policy 判定高风险
   │
   ▼
生成审批凭证(此时什么都没发生)
   │
   ▼
用户确认
   │
   ▼
放行 → 执行 → 返回操作回执

注意中间的"生成审批凭证"——在用户确认之前,系统什么都没做。 这一步是安全性的全部意义:权限不是"提示一下",而是"结构上就做不到"。


四、执行要能断电续跑

回到开头那个场景。Agent 要跑五步:

搜索资料 → 生成文章 → 保存草稿 → 加入专题 → 发布文章

在"保存草稿"之后模型超时了。如果没有执行状态,系统很可能从头再跑一遍。后果是:

  • 重复草稿、重复发布、重复扣费
  • 数据状态不一致(草稿存了两份,专题只加了一半)
  • 用户完全不知道完成到哪一步

生产系统要把任务拆成明确步骤,每一步的状态都落盘:

Step 1  检索资料      Completed
Step 2  生成内容      Completed
Step 3  保存草稿      Completed
Step 4  加入专题      Failed        ← 修复后从这里继续
Step 5  发布          Pending

三个基本概念

幂等:同一个操作执行两次,结果不能错误地重复。

create_note(requestId = "abc123")

同一个 requestId 重试时,不再创建第二篇笔记。实现上用一个键值存储挡一道:

public class IdempotentInvoker {
    private final IdempotencyStore store;

    public <T> T call(String key, Supplier<T> action) {
        Optional<IdempotencyRecord> hit = store.find(key);
        if (hit.isPresent()) {
            return hit.get().replayResult();   // 重放上次结果,不再执行
        }
        T result = action.get();
        store.save(key, result);
        return result;
    }
}

业务侧只要带着 requestId 调用:

public ToolReceipt deleteNote(String requestId, String noteId) {
    return idem.call("delete-note:" + requestId,
            () -> noteRepository.deleteAndRecycle(noteId));
}

Checkpoint:每完成一步,保存当前状态。

public class AgentRun {
    private final String runId;
    private final RunStatus status;       // PENDING / RUNNING / PAUSED / FAILED / COMPLETED
    private int cursor;                   // 当前执行到第几步
    private final List<TaskStep> steps;
}

public record TaskStep(
        String stepId,
        StepType type,        // RETRIEVE / GENERATE / SAVE_DRAFT / PUBLISH
        StepStatus status,    // COMPLETED / FAILED / PENDING
        Checkpoint checkpoint // 这一步完成后的状态快照
) {}

Resume:发生超时、模型故障或服务器重启后,从最后一个检查点继续。

public class RunResumer {
    public AgentRun resume(String runId) {
        AgentRun run = store.load(runId);
        List<TaskStep> pending = run.steps().stream()
                .filter(s -> s.status() != StepStatus.COMPLETED)
                .toList();

        for (TaskStep step : pending) {
            StepResult result = executor.execute(step);
            store.saveCheckpoint(run, step, result);   // 每步落盘
            if (!result.success()) {
                throw new StepFailed(step, result, retryPolicy.nextDelay());
            }
        }
        return run;
    }
}

重试本身要受控,不能无限循环烧钱:

public record RetryPolicy(
        int maxAttempts,
        Duration backoff,
        Class<? extends Throwable>[] retryOn   // 只重试可恢复的异常
) {}

这条链路的形状

任务开始
   │
   ▼
建 AgentRun(记录锚点)
   │
   ▼
Step 1 → 落 Checkpoint 1 → 完成
   │
   ▼
Step 2 → 落 Checkpoint 2 → 完成
   │
   ▼
Step 3 ── 超时 ✗
   │
   ▼
Resume:从 Checkpoint 2 继续,只重跑 Step 3 以后
   │
   ▼
幂等键兜底:Step 3 重试时不重复建草稿

五、Receipt:每个操作都要有回执

Agent 请求"删除笔记 123",执行完不能只返回一个 success

success 是无状态的、无法核验的。它回答不了:是谁删的?什么时候删的?删了能不能恢复?是不是重复执行?

要返回结构化回执(Receipt):

{
  "operationId": "op-789",
  "tool": "delete_note",
  "resourceId": "123",
  "status": "completed",
  "executedAt": "2026-08-04T10:20:00+08:00",
  "actor": "user-001",
  "recoverable": true
}
public record ToolReceipt(
        String operationId,        // op-789,全局唯一
        String tool,               // delete_note
        String resourceId,         // 123
        OperationStatus status,    // COMPLETED
        Instant executedAt,
        String actor,              // user-001
        boolean recoverable        // 是否支持撤销/恢复
) {}

它的用途:

  • 判断操作是否真的完成
  • operationId 防重复执行
  • 支持审计(谁在什么时候做了什么)
  • 支持撤销或恢复
  • 排查 Agent 为什么得到某个结果

一个具体的例子:删除进垃圾箱

你的系统里,删除笔记后进入垃圾箱。这时要把回执保留下来:

删除回执(op-789,note 123 → trash)
恢复回执(op-791,note 123 ← trash)
标签关联变化
索引更新状态

为什么需要这些?因为跨模块的不一致才是真正的坑。看这个事故:

public class RecycleBin {
    // 删除 = 移入垃圾箱 + 落一条回执
    public ToolReceipt delete(String userId, String noteId) {
        Note moved = noteRepo.moveToTrash(noteId, userId);
        vectorIndex.softDelete(noteId);          // 同步降权向量索引
        return new ToolReceipt(opId(), "delete_note", noteId,
                OperationStatus.COMPLETED, now(), userId, true);
    }

    // 恢复时带上原回执的 operationId,天然幂等
    public ToolReceipt restore(String operationId) {
        ToolReceipt origin = receiptStore.load(operationId);
        Note back = noteRepo.restoreFromTrash(origin.resourceId());
        vectorIndex.reindex(origin.resourceId());   // 索引一起恢复
        return new ToolReceipt(opId(), "restore_note", origin.resourceId(),
                OperationStatus.COMPLETED, now(), origin.actor(), false);
    }
}

如果没有这条回执链,会出现最恶心的情况:页面显示删除,但向量索引里还躺着这篇笔记——用户搜到一篇已删除的文章,点进去 404。有了回执,"删除"和"索引更新"这两个动作就有了同一个锚点,可以一起完成、一起补偿。

这条链路的形状

工具调用
   │
   ▼
执行业务(移入垃圾箱)
   │
   ▼
同步副作用(降权向量索引)
   │
   ▼
落 Receipt(operationId / actor / 时间 / 可恢复性)
   │
   ▼
返回给 Agent / 审计 / 幂等键 / 撤销凭证

六、Replay:问"为什么"的时候,能还原现场

用户一句"AI 为什么得出这个结论",是整个系统最难回答的问题。因为模型是概率性的——同一段输入,今天和明天的输出可能不一样。如果你不把现场存下来,你只能两手一摊。

Replay 的前提是把一次执行完整保存下来

使用的模型和版本
系统提示词版本
用户输入
Context Snapshot(模型当时看到的一切)
检索到的文档
工具调用请求
工具执行结果
最终回答
Token、延迟和费用
public record AgentTrace(
        String traceId,
        String modelId,              // 哪个模型、哪个版本
        String promptVersion,        // 提示词版本
        String userInput,
        ContextSnapshot snapshot,    // 当时喂给模型的完整上下文
        List<ToolCallRecord> toolCalls,   // 每个请求 + 对应回执
        String finalAnswer,
        CostMetrics cost             // token / 延迟 / 费用
) {}
public class ReplayService {
    public ReplayResult replay(String traceId) {
        AgentTrace trace = traceStore.load(traceId);
        CompiledContext ctx = snapshotStore.restore(trace.snapshotVersion());
        // 用当时的上下文,跑现在的模型 / 提示词
        return runner.run(ctx, trace.modelId());
    }
}

Replay 的三个用处

排查问题。先还原现场,再判断到底是哪一环:

  • 检索错了?
  • 上下文过期了?
  • 提示词有问题?
  • 模型判断错了?
  • 工具返回了错误?

模型升级回归测试。模型从 A 换成 B,把历史任务重新跑一遍,对比:

指标           模型 A    模型 B
准确率          92%      88%
工具调用成功率    98%      95%
P50 延迟        1.8s     2.1s
单次成本        0.032    0.041

这样"换模型"从"凭感觉拍板"变成"看数据做决定"。

建立评估集。从真实失败案例里挑任务,固定成测试集。以后改了提示词、改了 RAG、改了流程,不是靠感觉说"好像变好了",而是跑一遍回归测试看数字。

这条链路的形状

一次执行全程落 Trace
   │
   ▼
用户问"为什么"
   │
   ▼
按 traceId 找回快照 → 还原当时上下文
   │
   ▼
重新执行 / 对比新旧模型 / 跑评估集

七、经验要过审,才能成为规则

假设一次 Agent 执行成功了:

用户问 Java 问题 → 搜索前三篇文档 → 成功回答

能不能因此自动生成一条永久规则:“以后所有问题只搜索前三篇文档”?

不能。一次成功可能只是偶然。 更危险的是另一种做法——系统自己根据少数案例偷偷改变行为。一旦它开始这么干,你解释不了系统为什么变了,也没法回退。

正确流程是把"线上经验"变成"正式策略"之间,加一道完整的晋升管道:

线上执行
   │
   ▼
保存 Trace
   │
   ▼
形成候选案例
   │
   ▼
离线评估          ← 数据说话,不靠感觉
   │
   ▼
人工审核          ← 高风险的经验,人必须过目
   │
   ▼
确认有效
   │
   ▼
灰度发布(先小流量)
   │
   ▼
升级 Prompt、规则或检索策略

这叫做 Promotion:只有经过评估的经验才能晋升为正式策略。它保证了系统行为是可解释的、可回退的、可审计的。

public class PromotionPipeline {
    private final TraceStore traces;
    private final EvaluationService eval;
    private final HumanReviewQueue reviews;
    private final RolloutService rollout;

    // 线上 Trace 进来,够格的进人工审核队列
    public void ingest(AgentTrace trace) {
        if (eval.isCandidate(trace)) {
            reviews.submit(trace);
        }
    }

    // 审核通过,灰度发布,观察后全量
    public void promote(String reviewId, StrategyPatch patch) {
        if (reviews.approved(reviewId)) {
            rollout.apply(patch, canaryPercent = 5);
        }
    }
}

这条链路的形状

线上执行 → 落 Trace → 候选案例库 → 离线评估 → 人工审核
                                        │ 通过
                                        ▼
                        灰度发布 → 观察指标 → 晋升为正式策略

八、落地:先立边界,再加恢复,最后上治理

不需要一次性把整个 Agent 平台搭出来。分三阶段,每一阶段都有明确的边界。

MVP:先立四个边界

AIApplicationService
ContextBuilder
LLMGateway
ToolGateway

第一条链路:

Controller
   │
   ▼
AIApplicationService
   │
   ▼
ContextBuilder
   │
   ▼
LLMGateway
   │
   ▼
模型供应商

Agent 要调用业务能力时,走第二条链路:

Agent
   │
   ▼
ToolGateway
   │
   ▼
Application Use Case
   │
   ▼
Domain

这两条链路共同的红线是——Agent 不直接

  • 调 Repository
  • 操作数据库
  • 调用领域实体
  • 访问模型 SDK
  • 绕过权限系统

Phase 2:加恢复和可观测

AgentRun
ToolCallLog
ContextSnapshot
RetryPolicy
Timeout
IdempotencyKey

这一批解决的是第四、五节的问题:执行断了能续、重复了能挡、出了事能查。

Phase 3:上治理

Approval Workflow
Replay
Evaluation Dataset
Prompt Versioning
Model Comparison
Policy Engine

到这一步,才接近真正的生产级 Agent Runtime:审批、回放、评估、策略引擎全都在位。


把链路闭合

把每一节的局部链路拼回去,就是开头那张总领图的完整版:

用户目标
   │
   ▼
AIApplicationService
   ├─ 建 AgentRun(记录与恢复的锚点)
   │
   ├─ ContextBuilder 编译上下文
   │     身份 → 任务类型 → 检索 → 去重 → 排序 → 过滤 → Token 预算
   │
   ├─ LLMGateway 取模型的"建议"
   │     建议里的工具调用 → ToolGateway
   │       → PolicyEngine:权限 / 风险 / 审批 / Schema / 限额
   │       → 通过 → UseCase → Domain → DB
   │       → 返回 Receipt(可审计、可幂等、可撤销)
   │
   ├─ 每步落 Checkpoint → AgentRun
   │     失败 → 从断点 Resume(幂等重试兜底)
   │
   └─ 全程落 Trace → Replay(排查/回归/评估集)
                          │
                          ▼
                      Promotion 回路
                线上经验 → 评估 → 审核 → 灰度 → 晋升为策略

回到最开始的那句话:

把不确定的智能,限制在确定的软件工程边界里。

LLM 可以不稳定,但外围系统必须稳定。各自的职责分得很干净:

模型    负责建议
策略    负责授权
工具    负责执行
Runtime 负责记录与恢复
人      负责高风险决策

这也是 AI 系统和普通后端最大的区别:你不能假设模型每次都会做对,所以架构要围绕"它可能判断错误"来设计。 模型出错不可怕,可怕的是出错之后系统既不能发现、也不能恢复、还不能解释。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐