AI工程基础入门到精通
AI 工程基础入门到精通
这篇笔记解决什么
这篇是 AI Agent 目录的基础总论。它回答三个问题:
- AI 工程和传统 Java 后端工程到底差在哪。
- LLM、Prompt、Embedding、RAG、Tool Calling、MCP、Agent、Memory、Eval 这些概念在工程里各自负责什么。
- 怎么把这些概念串成一个真正能用的 Personal Knowledge Agent:能部署到服务器上长期跑、改动之后能用固定测试集验证有没有变差、出了问题能查出是哪一层的锅。
主线:
模型调用 → 上下文构造 → 知识检索 → 工具执行 → 状态机 → 记忆 → 观测评测 → 产品化
总览图
上图从下往上是依赖关系:越往下越接近基础设施,越往上越接近业务。下表列出每层要掌握的能力,这些名词后文会逐个展开,第一遍扫一眼有个印象就行。
| 层 | 核心能力 |
|---|---|
| Agent 层 | Workflow、Memory、Trace、Eval |
| 执行层 | Tool Calling、MCP、Permission |
| 知识层 | RAG、Embedding、Vector DB、Citation |
| 模型层 | Gateway、Streaming、Structured Output |
| 基础设施层 | 配置、日志、部署、成本 |
AI 工程和传统后端的区别
| 维度 | 传统后端 | AI 工程 | 工程后果 |
|---|---|---|---|
| 输出 | 程序确定返回 | 模型概率生成 | 必须有评测和约束 |
| 输入 | DTO / 参数 | 自然语言 + 上下文 | 必须做上下文构造 |
| 依赖 | DB / RPC / MQ | 模型 API / 向量库 / 工具 | 必须处理延迟和成本 |
| 失败 | 异常、状态码 | 编造、漏证据、工具误调 | 必须记录 trace |
| 测试 | 单元测试、接口测试 | eval set、golden answer、judge | 必须固定样例回归 |
| 安全 | 鉴权、SQL 注入 | prompt injection、越权工具、记忆污染 | 必须把模型当不可信输入 |
表里“测试”那一行的三个词解释一下:eval set 是一组固定的测试问题集,相当于你的单元测试用例集;golden answer 是每个问题预先人工确认过的标准答案;judge 是打分者,可以是精确比对的代码,也可以是另一个 LLM 来当裁判(业内叫 LLM-as-judge,让一个模型给另一个模型的回答打分)。
为什么需要 judge:模型输出是概率性的,同一个问题两次回答的措辞可能不同,很多场景没法用 assertEquals 硬比对,只能判断“意思对不对”。
“安全”那一行的 prompt injection(提示注入)指恶意指令被藏进模型会读到的内容里,比如一篇被检索到的笔记里写着“忽略之前所有指令”,后面安全一节会展开。
传统后端的核心是“输入确定,代码决定输出”。AI 工程的核心是“模型参与决策,但系统必须控制边界”。
大模型 LLM
工程视角下别把 LLM 当“智能大脑”,把它当成一个文本/结构生成器就好。这个生成器有三个麻烦特点:调用一次很贵、同样的输入两次输出可能不同、行为会被 prompt 里的内容带偏。系统设计基本都是围绕这三点做防御。
基本调用结构
sequenceDiagram
participant API as Backend API
participant G as Model Gateway
participant M as LLM Provider
API->>G: messages + tools + config
G->>M: normalized request
M-->>G: stream / tool_call / final text
G-->>API: normalized response
代码块收起展开
final class ModelRequest {
List<Message> messages; // system / user / assistant / tool
List<ToolSchema> tools; // 可选:模型能看到的工具定义
ModelConfig config; // model、temperature、maxTokens 等
String traceId; // 串起一次完整任务
}
final class ModelResponse {
String text; // 最终文本,可能为空
List<ToolCall> toolCalls; // 模型请求执行的工具
Usage usage; // input/output token 和成本
StopReason stopReason; // stop / length / tool_call / error
}常用参数
调模型 API 最常碰的几个参数,重点看“工程建议”列,都是踩坑之后的结论。
| 参数 | 作用 | 工程建议 |
|---|---|---|
model | 选择模型 | 不要写死,放配置 |
temperature | 控制随机性 | 学习/问答低一些,创作高一些 |
max_tokens | 输出上限 | 防止失控输出 |
top_p | 采样范围 | 初期少调,避免变量过多 |
stream | 流式输出 | 用户体验更好,但解析复杂 |
tools | 工具定义 | 必须配合服务端校验 |
失败模式
模型 API 会怎么挂、挂了怎么兜底,前四行跟你处理下游 RPC 超时限流是一个思路,最后一行是 AI 特有的:输出内容本身不可靠。
| 失败 | 表现 | 处理 |
|---|---|---|
| 超时 | 用户等待过久 | timeout + retry + fallback |
| 限流 | provider 返回 429 | 队列、退避、降级 |
| 输出过长 | 超出 UI 或预算 | max token + summary |
| 工具 JSON 坏 | 解析失败 | schema 校验 + 让模型修正 |
| 编造 | 无资料也答 | RAG 引用和拒答策略 |
“编造”就是常说的幻觉(hallucination):模型手里没资料也会一本正经地编个答案。对策是 RAG(后面有整节讲,简单说就是先检索资料再让模型基于资料回答并附引用)加上拒答策略:资料不够就明说“不确定”,不硬答。
Token 和上下文窗口
Token 是模型处理文本的基本单位,一个 token 大概对应半个到一个汉字,或一个英文单词的一部分,比如“上下文窗口”这四个字大约是 3 到 5 个 token。
上下文窗口是模型一次请求能看到的最大 token 数,128k 的窗口塞满大约是十几万汉字。
窗口是硬上限:系统提示、历史对话、检索资料、工具结果、用户问题加起来不能超,超了就得裁剪,所以下面要讲“预算”。
flowchart LR
S[System Prompt] --> C[Context Window]
H[History] --> C
R[RAG Evidence] --> C
T[Tool Results] --> C
U[User Input] --> C
C --> M[LLM]
上下文预算
窗口塞不下的时候按什么顺序取舍。思路类似线程池满了先保核心任务:优先级高的内容必须原样进 prompt,优先级低的先摘要压缩,再不行直接丢。
| 内容 | 优先级 | 原因 |
|---|---|---|
| 系统规则 | 高 | 控制行为边界 |
| 用户当前问题 | 高 | 当前任务核心 |
| 工具结果 | 高 | 最新观察 |
| RAG 证据 | 高 | 防编造 |
| 最近对话 | 中 | 保持连续性 |
| 长期记忆 | 中 | 个性化和状态 |
| 旧对话原文 | 低 | 成本高、噪声大 |
代码块收起展开
final class ContextPart {
String type; // system / user / evidence / tool_result / memory
String content; // 实际放入 prompt 的内容
int tokenEstimate; // 预算估算
int priority; // 数字越大越重要
String sourceId; // 来自哪条笔记、工具或记忆
}Prompt 工程
Prompt 没什么玄学,把它当成“给模型的运行时配置 + 任务说明 + 输出协议”来写就行,性质上接近你给下游服务写的接口文档:约定输入是什么、要做什么、按什么格式返回、异常时怎么办。
Prompt 结构
角色边界 → 任务目标 → 可用资料 → 可用工具 → 输出格式 → 禁止行为 → 失败时怎么说
模板示例
代码块收起展开
你是一个学习型 AI Agent。
只能基于给定资料回答;资料不足时必须说“不确定”。
回答必须包含:
1. 结论
2. 依据
3. 下一步建议
4. 来源Prompt 版本管理
| 字段 | 说明 |
|---|---|
prompt_id | 模板 ID |
version | 版本号 |
scenario | 适用场景 |
content | 模板正文 |
eval_score | 最近评测结果 |
created_at | 创建时间 |
Prompt 改动必须能回滚。原因:eval 分数掉了,你得能定位是哪一版 prompt 引起的,再切回上一版验证;如果 prompt 是直接改的字符串没有版本,分数变化就说不清是谁的锅,跟代码库不用 git 一个道理。
Embedding
Embedding 是把一段文本变成一个几百到几千维的浮点数向量,语义越接近的文本,向量距离越近。比如“Java 线程池参数怎么配”和“ThreadPoolExecutor 核心线程数设置”字面重合很少,但两者的向量会很接近,相似度检索就能把后者找出来。这是它比关键词搜索强的地方:按意思找,字面不重合也能命中。
flowchart TD
Text["文本"] --> Model["Embedding Model"]
Model --> Vector["向量"]
Vector --> Store[("Vector Store")]
Query["问题"] --> QModel["Embedding Model"]
QModel --> QVector["问题向量"]
QVector --> Search["相似度搜索"]
Store --> Search
关键理解
先解释表里的三个词。chunk 是把长文档切成的小段,是检索的最小单位;topK 是相似度搜索只取前 K 条结果,跟 SQL 的 LIMIT K 一个意思;rerank 是对召回的候选片段再做一次更精细的排序,粗召回负责快,rerank 负责准,类似先走索引筛一批再精确过滤。下面是新手最容易踩的误解。
| 误解 | 正解 |
|---|---|
| embedding 能判断真假 | 它只判断语义相似 |
| 向量库就是知识库 | 向量库只是索引 |
| chunk 越小越好 | 太小会丢上下文 |
| topK 越大越好 | 太大会污染 prompt |
| 相似就是有用 | 还需要 rerank 和引用检查 |
向量数据库
向量库的定位类似 MySQL 的索引:它只负责加速“按语义找到相关原文在哪”,原文本身、权限、版本管理这些还得靠你自己的存储和治理逻辑。别指望把东西塞进向量库就算“有知识库了”。
代码块收起展开
final class VectorRecord {
String chunkId; // 对应原文 chunk
float[] embedding; // 向量
String sourcePath; // 原始文件路径
String titlePath; // 标题层级
String hash; // 内容 hash,用于增量更新
long updatedAt; // 原文更新时间
}向量库的增删改查每一项都比普通 CRUD 多一层心眼,根源是索引和原文是两份数据,随时可能不同步:
| 操作 | 必须考虑 |
|---|---|
| insert | 去重、hash、批量写入 |
| update | 原文变更后重算 embedding |
| delete | 文件删除后清理索引 |
| search | metadata filter、topK、score |
| rebuild | 模型更换后全量重建 |
RAG
RAG 全称 Retrieval-Augmented Generation(检索增强生成):模型回答之前,先从你的私有资料里检索出相关片段塞进 prompt,让模型基于这些证据回答并标注来源。
它是私有知识问答的核心链路,比如“根据我的 Obsidian 笔记回答问题”就得走这一套。
下图里的 Rewrite 是把用户口语化的问题改写成更适合检索的形式,Recall 是召回候选片段,Citation 是在答案里标注引用来源。
好 RAG 的标准
这六条是验收清单,做完一个 RAG 功能逐条对照,缺哪条补哪条:
| 标准 | 说明 |
|---|---|
| 找得准 | 正确资料进入 topK |
| 引得准 | 引用真的支持结论 |
| 答得稳 | 不被无关片段带偏 |
| 会拒答 | 资料不足不编造 |
| 可复盘 | trace 记录 query、chunk、score |
| 可更新 | 笔记变化后索引更新 |
Tool Calling
Tool Calling 的流程:你在请求里声明有哪些工具可用(名字加参数 schema),模型觉得需要时输出一段“我想调 xxx 工具,参数是 {…}”的 JSON,真正执行由你的服务端完成,结果再回填给模型继续。
注意模型只能“请求”调用,执行权必须在服务端。就像前端传上来的参数永远要在后端重新校验一样,模型给的参数同样不可信。
flowchart TD
M["LLM 输出 tool_call"] --> R["Runtime 接收"]
R --> V["Schema + Permission"]
V --> T["Tool 执行"]
T --> Result["tool_result"]
Result --> M
代码块收起展开
interface Tool {
String name(); // 稳定工具名
JsonSchema inputSchema(); // 参数 schema
ToolRiskLevel riskLevel(); // READ / WRITE / DANGEROUS
ToolResult execute(ToolArgs args); // 服务端执行,不信任模型
}安全规则
把模型当成一个可能乱传参的外部调用方,每条规则都对应一种真实翻车方式:
| 规则 | 原因 |
|---|---|
| 参数必须校验 | 模型可能给错类型或越界 |
| 路径必须规范化 | 防止 .. 越权 |
| 写入要 diff preview | 防止误改 |
| 命令要白名单 | 禁止任意 shell |
| 工具结果要截断 | 防止上下文爆炸 |
| 每次调用要审计 | 出错能追责 |
MCP
MCP 全称 Model Context Protocol,是 Anthropic 提出的开放协议,作用是把工具、资源、提示模板用统一格式暴露给 Agent。
和上一节的关系:Tool Calling 是“模型会请求用工具”这个机制,MCP 是“工具怎么标准化对外提供”的协议,有点像 JDBC 之于各家数据库驱动,接口统一之后,任何支持 MCP 的 Agent 客户端都能接任何 MCP server。
flowchart LR
Agent[MCP Client / Agent] --> Server[MCP Server]
Server --> Tools[Tools]
Server --> Resources[Resources]
Server --> Prompts[Prompts]
| 对象 | 作用 |
|---|---|
| Tool | 可执行动作 |
| Resource | 可读上下文 |
| Prompt | 可复用任务模板 |
| Transport | 通信方式,如 stdio(标准输入输出管道,用于本地进程间通信)或 HTTP |
MCP 只是 Agent 生态的适配层:业务逻辑仍然写在你自己的服务里,MCP server 只是把这些能力包一层标准接口。
Agent
Agent 是“模型 + 工具 + 状态 + 控制循环”。跑起来大概是这样:用户问“帮我看看笔记里 JUC 部分还缺什么”,模型先输出一个搜索工具调用,runtime 执行后把结果回填,模型看了结果再决定是继续搜还是给最终回答,如此循环,直到产出答案或触发停止条件(比如最大步数)。
Agent 和普通 Chat 的区别
| Chat | Agent |
|---|---|
| 一问一答 | 多步执行 |
| 主要靠 prompt | 靠 runtime 控制 |
| 不一定有状态 | 必须有状态 |
| 不一定调用工具 | 会调用工具 |
| 难复盘 | 需要 trace |
Chatbot、Workflow、Agent、Multi-Agent
Hello Agents 与 Agent Learning Hub(两份社区流行的 Agent 入门教程)都把“先判断是否需要 Agent”放在入门阶段。区分四种形态只看一点:执行路径由谁决定。用了几个 LLM 说明不了形态。
| 形态 | 路径由谁决定 | 适合 | 不合格用法 |
|---|---|---|---|
| Chatbot | 用户一问、模型一答 | 对话、解释、单轮生成 | 把每次回答都包装成 Agent |
| Workflow | 程序预定义节点与分支 | 稳定、可预测的业务流程 | 为了“智能”把确定性步骤交给模型 |
| Agent | 模型根据观察动态选择动作 | 路径未知但工具边界明确的任务 | 没有 stop/permission 的无限循环 |
| Multi-Agent | Supervisor/协议分配独立子任务 | 上下文、权限或产物需要隔离 | 多个角色自由聊天、重复劳动 |
选择原则:能用普通代码就不用 Workflow;能用 Workflow 就不要强行 Agent;单 Agent 能完成时,不升级多 Agent。
三种经典 Agent 范式
Hello Agents 第 4 章用 ReAct、Plan-and-Solve、Reflection 建立经典范式。
先解释名字:ReAct 是 Reason + Act 的缩写,让模型每一步先推理再行动、看到结果再决定下一步;Plan-and-Solve 是先列出完整计划再逐步执行;Reflection 是让模型批评并修改自己上一轮的输出。
把它们当成三种应对不同失败的策略来用就行,没有规定要同时全开。
| 范式 | 核心循环 | 解决什么 | 主要风险 |
|---|---|---|---|
| ReAct | Reason -> Act -> Observe | 边观察边决定下一步 | 容易局部试错、重复调用 |
| Plan-and-Solve | Plan -> Execute Steps -> Update | 长任务先建立全局路径 | 初始计划错误会传播 |
| Reflection | Attempt -> Critique -> Revise | 用反馈改进上一轮结果 | 成本增加,自我批评不一定正确 |
工程上可以组合,但必须由状态控制:先用 Plan 建任务,单步执行仍可 ReAct,只有失败或质量门禁不通过时才 Reflection。不要每一步都同时规划、反思和重写。
Agent 与 Harness
Harness 原意是“马具”,在 Agent 圈里指套在模型外面的整套运行环境:模型是引擎,Harness 是底盘和操控系统。在本资料中,Harness 指模型周围的运行环境:
Harness = Tools + Context + Observation + Permissions + State + Feedback + Recovery
模型负责语义决策,Harness 负责让决策可执行、可限制、可恢复。同一模型在不同 Harness 下结果不同,因此失败归因时先检查上下文、工具、权限和反馈,再判断模型能力是否不足。综合学习地图见 23-Agent Harness工程-从最小循环到生产运行时。
Memory
Memory 是 Agent 的长期状态:跨会话保存的用户偏好、任务进度、历史决策。它和 RAG 是两回事:RAG 检索的是你的资料库,属于知识;Memory 存的是 Agent 自己积累的关于你和任务的事实,属于状态。一个像查文档,一个像老同事记得你上次说过什么。
mindmap
root((Agent Memory))
用户画像
偏好
背景
禁忌
项目状态
当前目标
已完成
待办
事件记忆
对话
工具结果
决策
反思记忆
失败原因
下次策略
技能记忆
成功流程
可复用脚本
记忆操作
| 操作 | 说明 |
|---|---|
| add | 新增事实 |
| update | 修正旧记忆 |
| delete / expire | 删除或失效 |
| retrieve | 按任务召回 |
| audit | 记录来源和原因 |
错误记忆比没有记忆更危险,所以 memory 必须可审计、可撤销。
AI Gateway
AI Gateway 是架在各家模型供应商 API 之上的统一网关,角色类似 Spring Cloud Gateway 之于下游微服务:业务代码只对着一个统一接口,路由、降级、限流、计费统一收敛在网关层,换模型供应商时业务代码不用动。
| 能力 | 作用 |
|---|---|
| 统一请求格式 | 屏蔽不同 provider 差异 |
| 模型路由 | 按任务选择模型 |
| fallback | 某模型失败时降级 |
| 限流 | 控制成本和并发 |
| 计费统计 | 记录 token 和费用 |
| 日志脱敏 | 避免敏感信息外泄 |
代码块收起展开
interface ModelGateway {
ModelResponse call(ModelRequest request); // 普通调用
Stream<ModelEvent> stream(ModelRequest request); // 流式调用
}结构化输出
让模型输出 JSON 不等于结构化可靠:它可能少字段、类型给错、编造不存在的枚举值,甚至 JSON 本身没闭合。所以拿到输出后必须逐层校验。
flowchart TD
LLM["LLM Output"] --> Parse["JSON Parse"]
Parse --> Schema["Schema Validate"]
Schema --> Business["Business Validate"]
Business --> Use["Use Result"]
Parse --> Repair["Ask Repair / Fallback"]
| 层 | 检查 |
|---|---|
| JSON parse | 是否是合法 JSON |
| schema validate | 字段、类型、枚举 |
| business validate | 路径、权限、范围 |
| post-check | 是否和任务目标一致 |
Eval
Eval 是 AI 工程的测试体系,对应你熟悉的单元测试加回归测试:维护一批固定的输入样例和判定标准,每次改动 prompt、模型或检索策略后全量跑一遍,用分数判断有没有退化,取代“肉眼看几个回答感觉还行”。
quadrantChart
title AI 功能质量象限
x-axis 低可控 --> 高可控
y-axis 低正确 --> 高正确
quadrant-1 可上线
quadrant-2 答得对但难维护
quadrant-3 不可用
quadrant-4 稳定但没价值
"RAG with citation": [0.82, 0.88]
"纯 prompt": [0.35, 0.55]
"无 trace Agent": [0.45, 0.7]
"带 eval workflow": [0.86, 0.84]
Eval 类型
链路上不同层的问题要用不同的 eval 抓,只测最终答案是不够的:
| 类型 | 测什么 |
|---|---|
| Golden QA | 固定问题答案 |
| RAG Recall | 正确资料是否召回 |
| Tool Eval | 工具选择和参数 |
| Workflow Eval | 多步任务是否完成 |
| Refusal Eval | 不知道时是否拒答 |
| Regression Eval | 改 prompt 后是否退化 |
Observability
Observability(可观测性)解决的问题:一次回答错了,你能查出是哪一层错的。做法是给每次任务发一个 traceId,把检索、模型调用、工具执行每个阶段的输入输出都记下来,类似后端的全链路追踪(SkyWalking 那一套),只是记录的内容换成了 prompt、召回片段和工具参数。
代码块收起展开
final class TraceEvent {
String traceId; // 一次任务 ID
String stage; // retrieve / model / tool / memory / final
Object input; // 当前阶段输入
Object output; // 当前阶段输出
long latencyMs; // 耗时
String error; // 错误信息
}| 要看什么 | 用途 |
|---|---|
| prompt | 模型到底看到什么 |
| retrieved chunks | RAG 是否找对 |
| tool calls | 工具是否选对 |
| memory hits | 记忆是否污染 |
| token usage | 成本是否失控 |
| stop reason | 为什么结束 |
安全
AI 安全没法做成一个单独的拦截器层,上下文、工具、记忆、输出每一环都有自己的风险点,要分别设防:
| 风险 | 例子 | 防线 |
|---|---|---|
| Prompt Injection | 笔记里写“忽略系统指令” | 资料当数据,不当指令 |
| Tool 越权 | 读写白名单外文件 | 路径规范化 + 根目录校验 |
| 记忆污染 | 把错误偏好长期保存 | 写入策略 + 审计 + 回滚 |
| 数据泄露 | 把私有笔记发到外部 | 脱敏 + 本地优先 |
| 过度自动化 | 自动删除/提交/转账 | 人工确认 |
成本和性能
pie title Agent 成本来源
"LLM 输入 token" : 35
"LLM 输出 token" : 20
"Embedding" : 10
"Rerank" : 10
"Tool latency" : 15
"Trace/Eval" : 10
优化手段
每一项优化都对着一个具体的花钱大户,先看账单再动手:
| 成本来源 | 优化 |
|---|---|
| prompt 太长 | context budget、摘要、topK 控制 |
| 重复 embedding | hash 缓存 |
| rerank 贵 | 先规则过滤,再模型 rerank |
| 工具慢 | 并发、缓存、超时 |
| eval 贵 | 分层评测,小集每日,大集每周 |
部署
本地 demo 变成长期跑的服务,至少检查这几项:
| 项 | 要求 |
|---|---|
| 配置 | API key、模型、向量库、路径白名单独立配置 |
| 日志 | trace 和普通业务日志分开 |
| 安全 | 工具权限默认最小 |
| 数据 | 私有笔记不要误传公开仓库 |
| 备份 | memory、trace、eval case 可备份 |
| 回滚 | prompt、模型、索引版本可回滚 |
生产运维闭环
Agent 上线后最怕两种假象:一是“能回答”被误当成“稳定可用”;二是“eval 通过”被误当成“线上不会退化”。
生产运维要把质量、成本、延迟、安全和回滚放在一起盯,任何一项单独看都会漏。下表先解释几个词:SLO 是服务质量目标(比如承诺“成功率 99%、p95 延迟 3 秒内”),p95Latency 指 95% 的请求延迟低于这个值,PII 指姓名、手机号这类个人敏感信息。
另外“关键证据”一列里 taskSuccessRate、costPerTask 这些 camelCase 名字是本文给指标起的字段名,不是业界统一标准,含义照英文字面理解就行。
其中两个不太好猜的:refusalAccuracy 是拒答正确率,该拒答的拒了、不该拒的没乱拒才算对;redactionRate 是脱敏覆盖率,日志落盘前敏感信息被抹掉的比例,secretLeakCheck 就是配套的密钥泄露检查。
| 运维对象 | 需要控制什么 | 关键证据 | 失控信号 |
|---|---|---|---|
| SLO | 成功率、延迟、拒答正确率 | taskSuccessRate、p95Latency、refusalAccuracy | 用户大量重试、长任务卡住 |
| 成本 | token、embedding、rerank、工具耗时 | tokenPerTask、costPerTask、toolLatency | 小问题消耗大上下文 |
| 限流 | 用户、模型、工具、MCP server | rateLimitHit、queueDepth、retryAfter | 429 暴增、队列堆积 |
| 缓存 | embedding、检索结果、工具只读结果 | cacheHitRate、contentHash、ttl | 旧证据、缓存污染 |
| 隐私 | PII、密钥、私有路径、原始 prompt | redactionRate、secretLeakCheck | trace 泄露敏感内容 |
| 降级 | 模型、RAG、工具、工作流 | fallbackReason、degradedMode | fallback 后仍假装完整成功 |
| 回滚 | prompt、索引、工具 schema、模型版本 | version、contentHash、rollbackTarget | 改完后无法定位退化来源 |
最小线上配置不要只有 API key,至少要有这些边界:
代码块收起展开
# agent-runtime.yml = 生产 Agent 的核心运行边界。
model:
primary: "fast-reasoner" # 主模型,用于常规任务。
fallback: "small-safe-model" # 降级模型,只处理低风险任务。
timeoutMs: 30000 # 单次模型请求超时。
budget:
maxInputTokens: 24000 # 单次请求最大输入上下文。
maxOutputTokens: 4000 # 单次回答最大输出。
maxCostPerTask: 0.20 # 单任务成本上限,超过要降级或拒绝。
rateLimit:
perUserPerMinute: 20 # 单用户限流。
toolCallsPerTask: 12 # 防止 Agent 无限调工具。
concurrentTasks: 8 # 防止并发把模型/工具打爆。
privacy:
redactSecrets: true # trace 落盘前脱敏密钥。
redactPrivatePaths: true # 对公开日志隐藏本地私有路径。
storeRawPrompt: false # 默认不长期保存完整原始 prompt。
rollback:
promptVersion: "agent-base-2026-07-06" # 当前 prompt 版本。
indexVersion: "notes-index-v3" # 当前检索索引版本。
toolSchemaVersion: "tools-v2" # 当前工具 schema 版本。生产事故排查顺序:
| 现象 | 先查 | 再查 | 不要先做 |
|---|---|---|---|
| 回答变差 | eval diff、promptVersion | RAG source、tool result | 直接重写 prompt |
| 延迟变高 | p95Latency、toolLatency | queueDepth、provider 状态 | 盲目换模型 |
| 成本变高 | tokenPerTask、topK、tool output bytes | cacheHitRate、rerank 次数 | 删除 trace |
| 安全告警 | tool audit、permission decision | prompt injection source | 只屏蔽用户输入 |
| 引用错误 | chunk hash、indexVersion | source range、dirty file | 只调温度参数 |
| 线上崩溃 | schema parse error、stopReason | fallbackReason、recent version | 继续放量 |
真正能上线的 Agent,必须能回答三个问题:
- 这次回答为什么贵、慢或错?
- 哪个版本的 prompt / 模型 / 索引 / 工具 schema 导致变化?
- 出问题时系统如何降级、拒答、回滚,而不是继续编?
AI 工程分层架构
flowchart TD
UI["UI / CLI"] --> App["Application Service"]
App --> Agent["Agent Runtime"]
Agent --> Context["Context / RAG"]
Context --> Data["Data Stores"]
Agent --> Tool["Tool / MCP"]
Tool --> Data
Agent --> Model["Model Gateway"]
Agent --> Trace["Trace / Eval"]
| 层 | 职责 |
|---|---|
| UI | 输入输出和交互 |
| Application | 业务入口、鉴权、任务管理 |
| Agent Runtime | 状态机、循环、停止条件 |
| Context | 拼装模型可见信息 |
| Tool | 工具注册、校验、执行 |
| MCP | 标准化外部能力 |
| RAG | 私有知识检索 |
| Memory | 长期状态 |
| Model Gateway | 模型适配、路由、fallback |
| Trace/Eval | 观测、评测、复盘 |
生产级 Agent 请求生命周期
别把一次 Agent 请求简化成“用户问题 -> 调模型 -> 回答”。生产级系统要把它当成一条受控事务:每一层都有输入、输出、失败模式和证据。这样做的价值是:回答错了以后能定位,不会只剩一句“模型不稳定”。
| 阶段 | 系统动作 | 核心工程对象 | 必须留下的证据 | 常见失败 |
|---|---|---|---|---|
| 1. 入口归一 | 接收 HTTP/CLI 请求,生成 traceId | AgentRequest、TraceContext | requestId、userInput、入口时间 | 参数缺失、请求过大 |
| 2. 任务路由 | 判断是问答、规划、检索、调试还是写入 | TaskType、RouteDecision | taskType、routeReason | 把规划任务当普通问答 |
| 3. 权限边界 | 判断本次可用工具、目录、写权限 | PermissionScope | allowedTools、deniedReason | 给了模型过大的工具权限 |
| 4. 上下文预算 | 选择历史、RAG、memory、tool result 的优先级 | ContextPart、token budget | 每段来源、优先级、token 估算 | 把无关历史塞满上下文 |
| 5. 检索证据 | 执行 BM25(经典关键词打分算法)/vector(向量相似度)/hybrid(两者混合)检索和 rerank | RetrievedChunk | chunkId、score、sourcePath | 召回相似但不相关的片段 |
| 6. Prompt 组装 | 拼 system、任务、证据、输出协议 | PromptSnapshot | promptVersion、最终消息列表 | prompt 改了无法回滚 |
| 7. 模型调用 | 通过 gateway 调 provider,解析 stream/tool_call | ModelRequest、ModelResponse | model、latency、usage、stopReason | 超时、限流、JSON 坏掉 |
| 8. 工具循环 | 校验 tool_call,授权,执行,回填结果 | ToolCall、ToolResult、ToolAudit | args、permission、result、error | 参数幻觉、越权、工具失败 |
| 9. 记忆决策 | 判断是否写长期记忆,写什么,不写什么 | MemoryRecord | source、scope、confidence、reason | 把临时聊天当长期记忆 |
| 10. 输出校验 | 校验结构、引用、拒答、敏感动作 | AnswerCheck | citations、schema result、risk flags | 无证据也强答 |
| 11. 评测复盘 | 把关键样例进入 eval/debug report | EvalCase、DebugReport | pass/fail、failureStage、nextFix | 只看最终答案,不知道哪层错 |
调试时按下面顺序查,不要一上来改 prompt:
| 现象 | 优先检查 | 判断标准 |
|---|---|---|
| 回答跑题 | RouteDecision + retrieved chunks | 是路由错,还是证据错 |
| 回答没引用 | prompt snapshot + answer check | 是没放证据,还是模型没按协议输出 |
| 工具执行错 | tool args + permission + result | 是模型参数错,还是服务端边界错 |
| 多步循环乱跑 | state transition + stopReason | 是否缺最大步数、失败出口或状态约束 |
| 改完变差 | eval report + promptVersion | 哪些固定样例退化,是否可回滚 |
从入门到精通路线
| Level | 能力 | 产出 |
|---|---|---|
| 1 | 能调模型 | Chat API |
| 2 | 能控输出 | Prompt version + JSON schema |
| 3 | 能读资料 | RAG with citation |
| 4 | 能用工具 | Tool Calling with audit |
| 5 | 能接协议 | MCP Server |
| 6 | 能多步执行 | Workflow Agent |
| 7 | 能长期记忆 | Memory write/update/delete |
| 8 | 能评测复盘 | Eval + Trace |
| 9 | 能产品化 | Deploy + README + Safety |
核心概念速查
| 概念 | 一句话 | 最容易混淆 |
|---|---|---|
| LLM | 生成模型 | 不等于业务逻辑 |
| Prompt | 模型任务说明 | 不等于代码约束 |
| Token | 模型文本单位 | 不等于字符 |
| Embedding | 语义向量 | 不判断真假 |
| Vector DB | 向量索引库 | 不等于完整知识库 |
| RAG | 检索增强生成 | 不等于长期记忆 |
| Tool Calling | 模型请求用工具 | 不等于模型能直接执行 |
| MCP | Agent 工具协议 | 不等于业务 API |
| Agent | 模型 + 工具 + 状态循环 | 不等于聊天机器人 |
| Harness | Agent 的工具、上下文、权限、状态与反馈环境 | 不等于模型本身 |
| Memory | 长期状态 | 不等于历史全文 |
| Eval | AI 测试系统 | 不等于主观体验 |
| Trace | 执行链路记录 | 不等于普通日志 |
最容易混的概念
RAG 和 Fine-tuning
Fine-tuning(微调)是拿你自己的数据继续训练模型,把知识或风格“烧进”模型权重里;RAG 不动模型,只在每次请求时把资料喂给它。两者解决的问题不同:
| 对比 | RAG | Fine-tuning |
|---|---|---|
| 目的 | 使用外部资料 | 改模型行为/风格 |
| 更新 | 改知识库即可 | 需要训练 |
| 可解释 | 可以引用来源 | 难解释 |
| 适合 | 私有知识问答 | 固定风格/任务适配 |
Tool Calling 和 MCP
| 对比 | Tool Calling | MCP |
|---|---|---|
| 层级 | 模型调用工具的机制 | 工具暴露协议 |
| 范围 | 单应用内也可用 | 跨 Agent 客户端 |
| 重点 | schema、执行、结果 | discover、transport、capability |
两列的重点词展开说一下。Tool Calling 关心的是单次调用本身:参数 schema 对不对、执行由谁来做、结果怎么回填给模型。
MCP 那列的三个词:discover 指 capability discovery(能力发现),客户端启动时先和 server 握手,问一句“你提供哪些工具和资源”,拿到清单再决定怎么用,类似微服务里客户端先从注册中心拉服务列表;transport 就是前面 MCP 一节讲过的通信方式(stdio 或 HTTP);capability 指 server 声明自己支持哪些能力,也就是握手时报出来的那份清单。
Agent 和 Workflow
| 对比 | Agent | Workflow |
|---|---|---|
| 路径 | 模型根据观察动态选择 | 程序预定义状态和步骤 |
| 优点 | 适应开放任务 | 稳定、可测、成本可控 |
| 风险 | 跑偏、循环、成本和副作用失控 | 遇到未建模情况不灵活 |
| 实践 | 用 Workflow/状态机约束边界 | 只把真正需要语义判断的节点交给模型 |
Agent 和 Workflow 可以同时存在于一个系统:生产系统通常用 Workflow 管确定性骨架,只把某些需要开放决策的节点交给 Agent。
最小知识闭环
flowchart TD
Learn["学概念"] --> Draw["画流程图"]
Draw --> Object["写工程对象"]
Object --> Demo["做 demo"]
Demo --> Eval["写 eval"]
Eval --> Note["整理笔记"]
Note --> Learn
每学一个概念,都要问:
| 问题 | 示例 |
|---|---|
| 它解决什么失败 | RAG 解决私有知识缺失 |
| 它的输入输出是什么 | query -> chunks |
| 它有什么状态 | index、trace、memory |
| 它怎么失败 | 召回错、引用假 |
| 它怎么评测 | recall(该召回的有没有召回)、faithfulness(答案是否忠于引用的资料) |
基础能力验收题库
学完这一篇,不要用“我好像懂了”判断。用下面这张表逐项自测:能画出对象、能说出失败、能写出最小实现,才算进入工程层面。
| 模块 | 必须能回答的问题 | 必须能写出的工程对象 | 不合格信号 |
|---|---|---|---|
| LLM | 模型请求里哪些字段会影响结果、成本和稳定性 | ModelRequest、ModelResponse、Usage、StopReason | 只会复制 SDK 调用,不记录 token/latency/error |
| Context | 模型这次到底看见了什么 | ContextPart、priority、token budget、sourceId | 把历史、资料、工具结果随手拼进 prompt |
| Prompt | prompt 改了以后如何判断变好还是变坏 | PromptTemplate、version、scenario、evalScore | 收藏模板,但没有版本和回归 |
| Embedding | 为什么相似不等于正确 | VectorRecord、chunkId、sourcePath、hash | 检索到相似片段就直接回答 |
| RAG | 资料不足时系统应该怎么做 | query rewrite、recall、rerank、citation check、refusal | 没引用、乱引用、不知道也硬答 |
| Tool Calling | 模型能不能直接执行工具 | Tool、schema、riskLevel、permission、audit | 模型输出什么参数就执行什么 |
| MCP | MCP 和普通 REST API 的边界在哪里 | tool/resource/prompt/transport/capability discovery | 把 MCP 当业务系统本身 |
| Agent | 多步任务什么时候继续、什么时候停止 | EngineState、maxSteps、StopReason、recover policy | 无限循环或失败后继续乱试 |
| Harness | 同一模型为什么在不同运行环境表现不同 | Tool/Context/Permission/State/Feedback/Recovery 边界 | 任务失败只会换模型或改 prompt |
| Memory | 什么信息值得长期保存 | MemoryRecord、scope、type、source、confidence、expireAt | 把聊天历史全文当记忆 |
| Gateway | 换模型为什么不应该改业务代码 | provider adapter、model route、fallback、rate limit | 模型供应商 SDK 散落在业务层 |
| Structured Output | JSON 输出为什么仍然不可信 | parse、schema validate、business validate、repair | JSON.parse 成功就直接入库/执行 |
| Eval | 改 prompt、模型、chunk 策略后怎么判断 | golden set、rag recall、tool eval、regression report | 靠主观看两三个回答判断 |
| Trace | 失败后如何复盘是哪一层的问题 | traceId、stage、input、output、latency、error | 只保存最终答案,无法重放过程 |
| Safety | 模型看到恶意资料时系统怎么防 | injection boundary、path allowlist、human approval | 把 RAG 内容当系统指令 |
| Deploy | 本地 demo 怎么变成可维护服务 | config、secret、index version、backup、rollback | key 写死、索引不可重建、prompt 不可回滚 |
每个模块至少做一个最小证明:
| 证明方式 | 合格产物 | 例子 |
|---|---|---|
| 画流程 | 能解释输入、处理、输出、状态 | RAG:query -> chunks -> answer/citation |
| 写对象 | 字段能说明职责和来源 | TraceEvent.stage 不是普通 log level |
| 写失败样例 | 能复现一个典型错误 | topK 召回错导致回答跑偏 |
| 写验收问题 | 能自动或半自动判断 | 资料不存在时必须拒答 |
| 写复盘结论 | 能改进下一版系统 | chunk 太短,改成标题层级切块 |
综合验收案例
用一个问题把基础能力串起来:
“根据我的 Obsidian 笔记,给我生成下一周 Agent 学习计划,并说明依据。”
合格系统不应该直接把问题扔给模型,而应该走下面这条链:
| 阶段 | 系统动作 | 必须留下的证据 | 失败时看哪里 |
|---|---|---|---|
| 输入理解 | 判断这是学习计划任务,不是普通问答 | taskType=learning_plan | 路由日志 |
| 上下文构造 | 加入用户目标、当前目录路线、最近学习记录 | ContextPart[] 顺序和 token 预算 | prompt snapshot |
| RAG 检索 | 找到路线、项目清单、周计划、基础概念 | query、chunkId、score、sourcePath | retrieved chunks |
| 计划生成 | 输出周目标、每日任务、验收方式 | structured output schema | schema error |
| 工具调用 | 可选:创建任务或写入计划笔记 | tool name、args、permission result | tool audit |
| Memory 写入 | 只保存稳定偏好和本周目标,不保存临时废话 | scope/type/source/confidence | memory audit |
| Eval 检查 | 检查是否有来源、是否可执行、是否过大 | eval case result | eval report |
| 最终回答 | 给结论、依据、下一步,不隐藏失败 | answer + citations + residual risk | final trace |
最后一行的 residual risk(残留风险)指答案里没能消除的不确定性,系统要在回答里明说,比如“这份计划基于 6 月的笔记生成,最近两周的学习记录缺失,进度可能需要你自己校准”,不能装作结论百分百可靠。
这个案例能同时检验:RAG 是否有引用、Tool 是否受控、Memory 是否克制、Agent 是否有状态、Eval 是否能回归、Trace 是否能定位。做不出这条链,就说明还停留在“会调模型”,没有进入 AI 工程。
精通判断标准
能说出很多框架名说明不了什么。真正掌握 AI 工程的判断标准是能做到下面十条:
- 设计一个 Agent 系统的分层架构。
- 写出每层核心对象和字段。
- 知道每层怎么失败。
- 能为失败设计 trace。
- 能写固定 eval cases。
- 能限制工具权限。
- 能让回答带来源。
- 能处理资料不足。
- 能记录和更新长期记忆。
- 能把系统部署成可维护项目。
建议先记住的主线
- LLM 负责生成,不负责事实可靠。
- RAG 负责给证据,不负责自动正确。
- Tool 负责行动,但必须由服务端控制。
- MCP 负责标准化暴露能力,不替代业务系统。
- Agent 负责多步任务,但必须由状态机约束。
- Memory 负责长期状态,但必须可审计和可撤销。
- Eval 负责判断质量,不要凭感觉。
- Trace 负责复盘,不要只保存最终答案。
把这条主线吃透,再学任何 Agent 框架都不会跑偏。