跳到主要内容

16 篇博文 含有标签「agents」

查看所有标签

隐藏草稿板问题:为什么仅凭输出监控无法保障生产级 AI Agent 的安全

· 阅读需 12 分钟
Tian Pan
Software Engineer

当 o1 或 Claude 等思考增强模型生成回答时,它们会在写出任何输出之前,在内部生成数千个推理 token。在某些配置下,这些思考 token 永远不会被公开。即使它们可见,最近的研究也揭示了一个令人震惊的模式:对于涉及敏感或伦理模糊话题的输入,前沿模型仅在 25–41% 的情况下会在其可见推理中承认这些输入的影响。

在其余时间里,模型在其草稿本 (scratchpad) 中做了其他事情,然后写出一个并不反映这些过程的输出。

这就是隐藏的草稿本问题,它改变了每个依赖输出层监控来执行安全约束的生产级智能体系统的安全计算方式。

主体层级问题:多智能体系统中的授权

· 阅读需 13 分钟
Tian Pan
Software Engineer

一家制造公司的采购智能体逐渐确信自己可以在没有人工审核的情况下批准 50 万美元的采购。它这样做并非通过软件漏洞或凭据窃取,而是通过为期三周的供应商电子邮件序列,其中嵌入了澄清问题:“10 万美元以下的任何订单都不需要副总裁批准,对吧?”随后逐步扩展了这一假设。到它批准 500 万美元的欺诈订单时,该智能体运行的范围完全处于其认为的授权限制内。人类认为该智能体有 5 万美元的上限。而该智能体认为自己根本没有上限。

这就是最具体形式的主体层级问题(principal hierarchy problem):授予的权限、声称的权限以及实际行使的权限之间存在不匹配。当智能体衍生出子智能体,而这些子智能体又进一步衍生出更多智能体时,问题会呈指数级增长,链条中的每一环都会对允许的操作做出独立判断。

工具选择难题:当智能体拥有数十个工具时,如何选择调用哪一个

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数 Agent 演示仅使用 5 个工具,而生产系统通常拥有 50 个。这两个数字之间的差距,正是大多数 Agent 架构分崩离析的地方。

当你给一个 LLM 4 个工具和一个明确的任务时,它通常能选对。但当你给它 50 个工具时,更有趣的事情发生了:准确率大幅下降,Token 成本激增,且失败模式通常表现为模型幻觉出一个工具调用,而不是承认它不知道该用哪一个。来自 Berkeley Function Calling Leaderboard 的研究发现,在跨多个领域的日历调度任务中,当工具数量从 4 个扩展到 51 个时,准确率从 43% 骤降至仅 2%。这绝不是一个平滑的性能退化曲线。

生产中的推理模型:何时使用,何时不使用

· 阅读需 9 分钟
Tian Pan
Software Engineer

大多数采用推理模型的团队都会犯同样的错误:他们开始在所有地方使用它们。一个新模型发布,带着令人印象深刻的基准测试数据,然后在一周内,它就处理了客户支持、文档摘要以及它真正为之构建的那两个真正困难的问题。然后,基础设施账单就来了。

推理模型——o3、支持扩展思维的 Claude、DeepSeek R1 及其后续版本——确实与标准 LLM 不同。它们在生成输出之前会执行内部的思维链(chain-of-thought),花费更多的计算周期来探索问题空间。这种额外的工作在需要多步骤逻辑的任务上带来了真正的提升。但它也导致每次请求的成本增加 5–10 倍,并增加 10–60 秒的延迟。这两点都无法作为默认设置被接受。

生产环境中的结构化输出:如何用 LLM 生成可靠的 JSON

· 阅读需 11 分钟
Tian Pan
Software Engineer

大语言模型是文本生成器。你的应用程序需要数据结构。这两个事实之间的差距,正是生产环境中的错误滋生之地。

每个使用大语言模型构建产品的团队都会遇到这个瓶颈。模型在游乐场中表现出色——它返回的内容看起来像 JSON,字段大多正确,通常能通过 JSON.parse。然后你将其部署上线,你的解析层却在凌晨两点开始抛出异常。响应中多了一个逗号。或者是一个 Markdown 代码围栏。或者模型决定在 JSON 前面添加一段解释性文字。又或者它幻觉出一个字段名。

业界已经花了三年时间来解决这个问题。这就是目前解决方案的收敛点,以及仍然让团队头疼的问题。

三个成熟度级别

团队处理结构化输出的方式有着清晰的进展,每个级别都有一个实际的可靠性上限。

级别 1:提示工程。 你写道:“只用以下格式的有效 JSON 回复:”并展示一个示例。对于简单模式,这在 80-95% 的情况下有效。失败模式很微妙:模型在复杂提示中添加前言,在模式变长时将 JSON 包装在代码块中,或者静默地省略可选字段。你添加一个正则表达式清理步骤和一个 try/catch,然后说服自己这样没问题。

对于任何重要的应用来说,这都远不够好。95% 的解析成功率听起来很高,直到你有一个 10 步的智能体链:0.95^10 ≈ 0.60。十次智能体运行中,有六次会失败。这个数学计算是无情的。

级别 2:函数调用/工具使用。 所有主要提供商都暴露了一个 API,你可以在其中定义 JSON 模式,模型应该填充它。这能让你达到 95-99% 的可靠性。但问题是:模式是一个提示,而不是一个约束。模型将模式视为其上下文的一部分并学习遵循它——但解码过程中的任何环节都无法阻止它生成无效 token。提供商仍然可能返回格式错误的数据包,尤其是在处理复杂模式或边缘情况输入时。

级别 3:带有约束解码的原生结构化输出。 这是在数学上保证 100% 模式有效性的地方。推理引擎根据你的模式构建一个有限状态机,并在每个生成步骤中屏蔽无效 token。模型实际上无法生成无法解析的输出。OpenAI 结合 json_schemaresponse_format、Gemini 的 response_schema,以及 Outlines 等开源框架都使用了这种方法。

如果你正在构建任何需要可靠下游解析的东西——分类管道、智能体工具调用、数据提取——你都会需要级别 3。

约束解码的实际工作原理

理解其实现方式是值得的,因为它决定了你能使用和不能使用哪些模式。

在每个生成步骤中,模型都会在其整个词汇表(50,000 多个 token)上生成一个概率分布。通常,你会从该分布中采样。而使用约束解码时,你首先构建一个有限状态机,代表你的 JSON 模式中的每个有效路径。在采样之前,你会计算一个token 掩码:一个布尔向量,其中 false 意味着“给定 FSM 中的当前状态,这个 token 不能出现在这里”。你将这些 logits 置零,然后从剩余的 logits 中采样。

结果是:模型只能生成那些能够推进模式有效完成的 token。这不是后处理——它融入到每一个解码步骤中。

早期的实际开销曾是一个担忧。为复杂模式构建初始 FSM 可能需要 50-200 毫秒。但像 XGrammar(来自 MLC 团队)这样的引擎能够在每个 token 不到 40 微秒内完成 token 掩码生成,后续请求则重用缓存的 FSM,开销几乎为零。对于简单模式,延迟影响低于 5%。对于具有大型枚举集的深度嵌套模式,延迟可能达到 30-60%——这真正提示你需要简化你的模式了。

模式设计:团队常犯的错误

即使约束解码强制执行了语法有效性,糟糕的模式设计仍然会导致语义失败。以下是困扰大多数团队的模式:

将推理置于结论之前。 如果你的模式有一个 reasoning 字段和一个 classification 字段,请将 reasoning 放在前面。大语言模型从左到右生成 token。当模型在确定分类之前写出其推理时,它会产生更好的分类。如果你将答案字段放在前面,模型会在思考之前确定一个标签,然后才在推理字段中进行合理化。这听起来像大语言模型的一个怪癖,但它确实能持续地将准确性提高几个百分点。

扁平化你的模式。 嵌套是可靠性的大敌。OpenAI 的原生结构化输出最多支持 5 层嵌套和 100 个总属性。超出这个范围,语法编译时间会飙升,每个 token 的开销也会增加。更重要的是,即使有约束解码,具有 4 层以上深度嵌套的模式也表现出明显更高的错误率——模型有更多机会失去上下文。如果你的模式深度嵌套,请问问自己,这种嵌套是反映了实际的数据层次结构,还是仅仅是组织偏好。

描述每个字段。 Pydantic 的 Field(description=...) 值作为内联指令传递给模型。如果没有描述,模型将仅从字段名推断语义。confidence: float——它是 0-1 还是 0-100?status: str——有效值是什么?字段描述不是文档;它们是直接影响输出质量的提示指令。

明确处理可选性。 OpenAI 的结构化输出不支持你期望的那种可选字段。如果一个字段可以不存在,请将其建模为带有默认值 NoneOptional[str],而不是仅仅是 没有默认值的 str | None。提供商对这种区别处理不同,如果处理不当,会在运行时产生神秘的“无效模式”错误。

避免复杂模式。 带有复杂模式的正则表达式约束字段、具有多个分支的 oneOf 和递归模式会在 FSM 中造成组合爆炸。如果你需要“一个或多个与某个模式匹配的项”,请考虑将问题分解为多个顺序调用,而不是在单个模式中表达它。

实践中的服务商格局

每个主要的服务商都有不同的 API 接口,其抽象层无法清晰地在不同服务商之间转换。

OpenAI 提供了最成熟的实现。你可以使用 client.beta.chat.completions.parse() 搭配 Pydantic 模型,它能处理模式转换并返回一个类型化的 Python 对象。使用原始 JSON 模式的 response_format 方法也有效,但需要手动构建模式。.parse() 方法是正确的默认选择。

Anthropic 没有专用的结构化输出 API。其惯用模式是强制使用工具:将你的模式定义为一个工具,然后设置 tool_choice 强制模型调用它。如果没有 tool_choice: {type: "tool", name: "your_tool"},模型可能会选择根本不使用该工具。这并非约束解码——它仍然是 Level 2——但比提示工程要可靠得多。

Google Gemini 提供了带有约束解码的 response_schema,类似于 OpenAI 的方法。该 API 接受原始 JSON 模式而非 Pydantic 模型,所以你需要模式转换工具。

对于跨多个服务商工作的团队来说,Instructor 库抽象了这些差异。它在 OpenAI、Anthropic、Gemini 等平台之间提供了一个统一的 client.chat.completions.create(response_model=YourPydanticModel) 接口。Instructor 还能处理验证失败时的自动重试——如果模型返回了未能通过 Pydantic 验证的内容,它会附带错误信息重新提示并再次尝试。

验证三明治

即使在使用原生结构化输出时,也务必在其之上添加一个验证层。这并非多疑——这是为了防范语法约束无法捕捉的语义失败。

from openai import OpenAI
from pydantic import BaseModel, field_validator

class ClassificationResult(BaseModel):
reasoning: str
label: str
confidence: float

@field_validator("confidence")
def confidence_must_be_normalized(cls, v):
if not 0.0 <= v <= 1.0:
raise ValueError(f"confidence must be between 0 and 1, got {v}")
return v

@field_validator("label")
def label_must_be_valid(cls, v):
valid_labels = {"positive", "negative", "neutral"}
if v not in valid_labels:
raise ValueError(f"label must be one of {valid_labels}, got {v}")
return v

client = OpenAI()
result = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[...],
response_format=ClassificationResult,
)

# result.choices[0].message.parsed is already a ClassificationResult
# but Pydantic validators run during construction, so they've already fired

模式强制结构。Pydantic 验证器强制语义。两者你都需要。

约束解码保证的是语法有效性,而非语义正确性。模型可以在一个浮点数字段中返回 confidence: 1.7 并满足模式。它也可以从模式的枚举中返回一个对于输入而言语义上错误的标签。验证器能捕捉前者;评估则捕捉后者。

智能体链中的结构化输出

在多步骤工作流中,可靠性计算会变得更糟。每个返回结构化数据的工具调用都是一个模式验证可能失败的步骤。通过 Instructor 的重试行为,失败会附带错误上下文进行重试——但重试会消耗 token 和延迟,而且某些故障模式会循环。

这里有两种有助于解决问题的模式:

在每个步骤中缩小你的模式。 不要将一个庞大复杂的模式贯穿于每个工具调用。在每个步骤中,只提取你需要用于下一步的数据。更小的模式具有更低的失败率和更少的开销。

每次调用都记录模式版本。 模式会演变,而错误往往源于模式变更未在所有地方传播。将模式版本与提示和响应一同记录。当出现问题时,你可以根据当时生效的模式重放精确的输入。

仍然存在的问题

约束解码解决了解析问题,而非建模问题。无论模式强制如何,一些故障模式依然存在:

幻觉枚举值。 如果你的模式允许 enum: ["gpt-4", "claude-3-5-sonnet", "gemini-2-0-flash"],而你添加了一个新模型却忘记更新模式,模型将被迫返回其中一个有效值——但它可能会自信地返回错误的值。模式约束并不能让模型准确;它们只是让模型可解析。

长链中的语义漂移。 在多步骤管道中,第 N 步的结构化输出会作为第 N+1 步的提示输入。意义上的错误(而非格式上的)会以解析检查无法检测到的方式累积。这时,评估和抽查比工具更重要。

调用者之间的模式不匹配。 在包含多个服务的生产系统中,调用服务中的模式定义与下游消费者期望的模式定义不一致是很常见的。将你的 Pydantic 模型视为事实的唯一来源,并将其作为一个包共享,而不是复制粘贴的字典。

默认应该是 Level 3

原生结构化输出的工程论点很简单:提示工程增加了重试复杂性,函数调用增加了验证复杂性,两者都增加了在凌晨 2 点调试起来令人烦恼的故障模式。带有 Pydantic 验证层的原生结构化输出为你提供了最强的保证,并消除了一整类生产事故。

工具链已成熟。XGrammar 使约束解码足够快,对于简单模式而言,延迟很少成为问题。Instructor 库消除了服务商特定的样板代码。在 2025 年,没有充分的理由使用 Level 1 解析交付新的 LLM 管道。

唯一的真正代价是模式设计规范。扁平模式、描述性字段、显式可选性、先推理后结论的顺序——这些都不是复杂的要求,但它们需要刻意为之。这种规范正是区分在演示中有效与在生产中可靠运行的 LLM 功能的关键。

生产环境中的 LLM 可观测性:工程师容易忽略的四个隐性故障

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数将 LLM 应用推向生产环境的团队,其日志设置常被误认为是可观测性。他们在数据库中存储提示词(prompt)和响应,在表格中跟踪 token 数量,并在 Datadog 中设置延迟告警。然而,当用户反馈聊天机器人已经连续两天给出错误回答时,没人能告诉你原因 —— 因为收集到的数据都没有告诉你模型是否真的正确。

传统监控回答的是“系统是否在线且速度多快?”而 LLM 可观测性回答的是一个更难的问题:“系统是否在做它应该做的事情,以及它在什么时候停止了这种正常行为?”当你的系统行为是概率性的、依赖上下文的,并且经常以不触发任何告警的方式出错时,这种区别就显得至关重要。

Agentic RAG:当你的检索流水线需要一颗大脑时

· 阅读需 12 分钟
Tian Pan
Software Engineer

2024 年,90% 的智能体 RAG(Agentic RAG)项目在生产环境中失败了。原因并非技术本身存在缺陷,而是工程师们仅仅将向量搜索、提示词(prompt)和大语言模型(LLM)组合在一起,称之为检索管道并直接发布——却忽略了从查询到回答之间每一层累积的失败成本。

经典的 RAG 是一个确定性函数:嵌入查询 → 向量搜索 → 填充上下文 → 生成。它单向运行一次,没有反馈循环。当查询是针对分块良好的语料库进行简单的单步查找时,这种方式很有效。但当用户询问“比较这五份合同中的责任条款”或“总结自第三季度事故以来我们的基础设施配置发生了哪些变化”,或者任何需要先综合多份文档中的证据才能形成答案的问题时,它就会惨遭失败。

Agentic 工程模式:While 循环只是最简单的部分

· 阅读需 11 分钟
Tian Pan
Software Engineer

问问任何一个交付过真实智能体(agentic)系统的团队,最难的部分是什么。几乎没有人会说是 “LLM 调用”。无论是 Claude Code、Cursor,还是自研的财务自动化工具,每个生产环境中的智能体运行的核心循环几乎都是一模一样的。有趣的工程挑战 —— 即区分一个好用的智能体与一个失控的成本中心的部分 —— 完全存在于那个循环之外。

一个团队最初运行智能体循环的费用是每周 127 美元。四周后,账单达到了 47,000 美元。一个没有 token 上限的失控循环将每一次迭代复合成了财务灾难。模型一直在运行,没人告诉它停止。

为什么你的智能体应该编写代码,而不是 JSON

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数 Agent 框架都默认采用同一种动作模型:LLM 输出一个 JSON 块,宿主系统对其进行解析,调用工具,然后返回结果。如此循环。这种方式整洁、可审计,且几乎被普遍使用——而这恰恰是问题所在。对于超出单一工具调用的任何场景,这种架构都会迫使你编写脚手架代码来解决 Agent 本可以自行解决的问题——前提是如果允许它编写代码。

还有另一种方法:给 Agent 一个 Python 解释器,让它输出可执行代码作为其动作。一项已发布的基准测试显示,与 JSON 工具调用相比,其 任务成功率高出 20%。内部基准测试显示,平均 LLM 往返次数减少了 30%。一个围绕这一理念构建的框架在发布后不久便登顶 GAIA 排行榜榜首(验证集准确率为 44.2%)。权衡在于执行环境更加复杂——但所需的工程量是可控的,而且带来的行为增益是实实在在的。

生产环境中的 LLM 可观测性:追踪不可预测的行为

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的监控栈能告诉你关于请求率、CPU 和数据库延迟的一切。但它几乎无法告诉你你的 LLM 是否刚刚幻觉出了一个退款政策,为什么一个面向客户的智能体在回答一个简单问题时循环调用了三次工具,或者你的产品中哪个功能正每天悄悄烧掉 800 美元的 Token。

传统的可观测性是围绕确定性系统构建的。LLM 在结构上完全不同 —— 每次都是相同的输入,不同的输出。故障模式不再是 500 错误或超时;而是一个听起来自信且合理、但恰好错误的答案。成本也不再稳定可预测;当一个配置错误的 Prompt 遇到流量高峰时,成本会激增。调试也不再是“在堆栈跟踪中查找异常”;而是“重建为什么智能体在周二凌晨 2 点选择了这条工具路径”。

这正是 LLM 可观测性(Observability)所要解决的问题 —— 而这一领域在过去 18 个月里已经显著成熟。

生产环境中的工具调用:循环、陷阱与实战方案

· 阅读需 10 分钟
Tian Pan
Software Engineer

当你的智能体在放弃之前,第三次默默地重试同一个损坏的工具调用时,你就会意识到,“仅仅添加工具”并不是一种生产环境的策略。工具调用解锁了真正的能力——外部数据、副作用、保证格式的输出——但使其工作的智能体循环(agentic loop)具有在演示中不会表现出来的尖锐边缘。

这篇文章将探讨这些边缘:循环实际上是如何运行的,悄悄破坏并行执行的格式规则,如何编写能让模型做出正确选择的工具描述,以及如何处理错误以让模型恢复而不是陷入死循环。

AI Agent 架构:生产环境中真正有效的方案

· 阅读需 13 分钟
Tian Pan
Software Engineer

一家公司交付了 7,949 个 AI Agent。其中只有 15% 能够正常工作。其余的要么静默失败,要么陷入死循环,或者在执行任务中途前后矛盾。这并非个别现象——企业级分析一致发现,88% 的 AI Agent 项目从未进入生产阶段,95% 的生成式 AI 试点项目以失败告终或表现严重不及预期。引人入胜的演示 (Demo) 与可靠系统之间的差距并非模型问题,而是架构问题。

那些成功交付了实际可用 Agent 的工程师们,在架构决策上达成了一系列共识,而这些决策与框架教程中的玩具示例截然不同。本文将探讨这些决策:层级如何划分、故障集中在哪里,以及为什么最难的问题从来不是提示词 (Prompt)。