从演示到落地:构建安全可控的AI Agent工程实践
2026/9/6 19:02:35 网站建设 项目流程

近一年的 AI Agent 演示视频,越来越像一场“极限测试秀”。模型厂商不再满足于让 Agent 写周报、订机票,而是刻意把 Agent 丢进复杂、模糊甚至带点危险的场景里:让 Agent 自己调用支付接口、自动访问外部网站、连续执行多步 API 操作,然后拍摄它一步步“闯祸”或者“意外成功”的过程。这类内容传播效果非常好,但真正的 Agent 工程问题也被掩盖了:演示中的 Agent 为什么能完成任务?失败时为什么没有兜底?换一个评测集,它还能复现吗?作为开发者,比起围观“谁更能闯祸”,更值得做的是拆解 Agent 的规划、工具调用、记忆和评测链路,把营销剧本变成可验证的工程指标。

这篇文章会从 Agent 核心概念讲起,再用一个最小可运行案例展示如何给 Agent 添加工具调用和人工审批,接着聊聊评测指标、失控原因、RAG 链路中常见部署问题,最后给出适合开发者自检的安全清单。整篇文章不会站在某个厂商立场,而是从“如何让 Agent 在真实项目中稳定执行任务”的角度展开。

1. Agent 的“闯祸”能力,为什么总被当成卖点

1.1 营销剧本里的 Agent:冲突越大,传播越强

模型厂商选择演示场景时,天然倾向于选“有冲突感”的任务。一个 Agent 安安稳稳地整理文件,拍出来没有传播力;一个 Agent 自作主张调用外部服务、反复重试、最终导致非线性结果,拍出来就很容易引发讨论。这种营销手法本质上是在放大 Agent 的能力边界,同时弱化它的失败概率和人工干预过程。

从技术传播的角度看,这是有效的内容策略,但也是误导性的。演示视频通常不会展示以下几类信息:

  • 评测任务的具体数量和难度分布。
  • Agent 是否使用了预置的“成功路径”。
  • 模型失败了多少次才选中这次录制。
  • 人工确认、限制条件、步骤超时等隐藏机制。

如果你是准备把 Agent 接入业务系统的开发者,不能把演示视频当技术方案。演示回答的是“这个模型能不能表达复杂的 Agent 行为”,而工程方案要回答的是“在给定数据、接口和权限条件下,Agent 能保证多高的任务完成率和安全性”。

1.2 真实 Agent 的底层能力:规划、记忆、工具调用和反思

抛开营销,Agent 的技术定义并不神秘。它是指以大语言模型为决策核心、通过感知环境、制定计划、调用工具、观察结果来完成目标的软件系统。落到工程实现上,核心组件通常包括四个部分:

组件作用典型问题
规划模块把大任务拆成多步子任务拆解过度或遗漏关键步骤
记忆模块保存关键上下文和历史结果上下文漂移、关键信息被覆盖
工具调用模块以结构化参数调用 API 或函数参数幻觉、调用未授权工具
反思模块根据执行结果修正下一步计划反思流于形式、重复同样的错误

一个能“闯祸”的 Agent,往往是这四个模块之间的约束没做好。例如规划模块生成了过于开放的步骤,工具调用模块又缺少参数白名单,模型很可能在某个分支里调用了一个不应该执行的接口。再比如反思模块不断重试同一失败操作,直到触发成本上限或死循环。

1.3 把“闯祸”转换成可度量问题的关键一步

营销视频里的“闯祸”是戏剧化表达,工程里的“闯祸”可以转换为可度量的问题:任务成功率下降、工具调用异常率上升、安全违规次数增加、成本超预算、响应时间超出可接受范围。只要指标定清楚,Agent 行为是否可靠是可以横向比较的。

因此,在你真正动手写 Agent 之前,建议先回答三个问题:

  1. Agent 允许调用哪些工具?每个工具的调用条件是什么?
  2. Agent 需要多少步骤才能完成任务?最大步数是多少?
  3. Agent 失败后应该重试、求助还是终止?由谁决定?

这三个问题定义清楚,后面所有代码和评测都有依据。营销剧本不会告诉你这些约束,但真实项目必须自己设计。

2. 从零构建一个“能干活但不会闯祸”的最小 Agent

2.1 选型:先定推理入口,再选 Agent 框架

构建 Agent 的第一步不是选框架,而是选定模型推理入口。常见的推理入口有两类:云端模型 API 和本地推理服务。云端 API 集成快,但需要把业务数据传给外部服务;本地推理可以用 vLLM、Ollama、LM Studio 等工具部署,数据可控,但环境配置成本更高。

选型时建议按这个顺序判断:

  1. 数据是否允许出内网。
  2. 请求延迟和吞吐要求。
  3. 是否依赖 Embedding、Reranker 等辅助模型。
  4. 团队是否具备 GPU 运维能力。

如果选择云端 API,后续例子可以直接调用 OpenAI、Anthropic、百炼等服务的兼容接口。如果选择本地推理,需要额外处理模型格式、张量并行、显存占用、推理服务与 Agent 框架之间的协议兼容。

2.2 项目结构和依赖准备

下面以一个最小可运行案例为例。项目不需要复杂框架,只使用 Python 标准库加一个模型调用函数,便于理解 Agent 的“规划-调用-观察-终止”循环。实际项目建议使用 LangChain、LlamaIndex 或自研调度层,但核心思路相同。

目录结构:

agent_demo/ ├── main.py ├── requirements.txt └── tools/ └── registries.py

requirements.txt 示例:

openai>=1.30.0

如果你使用本地推理服务,可以不安装 openai 包,直接使用对应服务的 HTTP 客户端。下面代码中以 OpenAI SDK 作为接入示例,只是为了说明通用接口。

2.3 核心实现:一个带工具调用和人工确认的 Agent

先定义工具注册表。不要使用globals()做动态函数调用,生产环境应该用字典映射:

# tools/registries.py from typing import Any, Callable def query_order(order_id: str) -> str: # 实际项目中替换为数据库查询或订单 API 调用 return f"订单 {order_id} 当前状态: 已发货" def send_notification(content: str) -> str: # 实际项目中替换为消息网关 API return f"已发送通知: {content}" TOOL_REGISTRY: dict[str, dict[str, Any]] = { "query_order": { "description": "查询订单状态,参数 order_id 为订单编号", "handler": query_order, }, "send_notification": { "description": "发送通知消息,参数 content 为通知内容", "handler": send_notification, }, } def get_tool_schema() -> str: import json schema = {} for name, info in TOOL_REGISTRY.items(): schema[name] = info["description"] return json.dumps(schema, ensure_ascii=False)

再实现主循环。模型先决定调不调用工具,如果调用工具,则返回结构化工具调用信息。Agent 拿到工具调用后,先做权限检查,再请求人工确认,执行完成后把结果放回消息列表,进入下一轮:

# main.py import json from tools.registries import TOOL_REGISTRY, get_tool_schema def call_llm(messages: list[dict]) -> dict: # 此处以 OpenAI SDK 为例,实际项目替换为你的模型服务 from openai import OpenAI client = OpenAI() resp = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=[], ) # 实际项目中解析 response,得到 content 或 tool_call return {"content": resp.choices[0].message.content} def run_agent(user_input: str, max_steps: int = 3) -> str: system_prompt = f""" 你是一个任务执行助手。你只能使用以下工具: {get_tool_schema()} 处理流程: 1. 判断是否需要调用工具。 2. 如果不需要,直接回答用户。 3. 如果需要,返回工具名和参数。 4. 每轮只能调用一个工具,等待工具结果后再继续。 禁止调用未注册的工具。如果任务无法完成,直接说明原因。 """.strip() messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input}, ] for step in range(max_steps): response = call_llm(messages) content = response.get("content", "") if not content: return content try: decision = json.loads(content) except json.JSONDecodeError: # 如果模型直接输出自然语言,说明任务完成 return content tool_name = decision.get("tool") arguments = decision.get("arguments", {}) if tool_name not in TOOL_REGISTRY: raise PermissionError(f"Agent 尝试调用未注册工具: {tool_name}") print(f"\n步骤 {step + 1}: 模型请求调用 {tool_name},参数: {arguments}") confirm = input("确认执行? [y/N]: ").strip().lower() if confirm != "y": return "已由人工终止工具调用" result = TOOL_REGISTRY[tool_name]["handler"](**arguments) messages.append({"role": "user", "content": f"工具 {tool_name} 返回: {result}"}) return "已达到最大步骤数,自动终止" if __name__ == "__main__": print(run_agent("请帮我查询订单 1001 的状态"))

这段代码的关键点有三处:

  • 工具白名单:只有注册在TOOL_REGISTRY中的函数才能被调用,防止模型任意执行不存在的函数。
  • 人工确认:默认不执行,必须输入y才会往下走。生产环境可以把这里的终端输入替换成审批系统或工单系统。
  • 最大步数限制:max_steps=3能避免 Agent 陷入无限循环。

需要注意的是,上面代码中让模型返回 JSON 的方式只是教学示例。真实项目应该使用大模型 API 内置的 tool calling 参数,让返回格式稳定,避免靠 JSON 解析猜测。如果不使用内置工具调用,模型很可能返回 Markdown 代码块或自然语言,导致解析失败。

3. 评测不能只看演示,要建立可复现的 Agent 评测基线

3.1 为什么演示效果好不等于评测分数高

模型厂商挑选的演示任务通常只有几条路径,而且已经事先过滤了失败样本。真实业务中的情况完全不同:用户输入格式多变,接口可能超时,权限可能不足,数据可能缺失。Agent 在这些条件下要稳定完成任务,必须经过可复现的评测。

评测的目的是回答四个问题:

  • 任务是否完成。
  • 完成过程中是否遵守了约束。
  • 消耗了多少时间和 Token。
  • 是否触发了安全违规。

3.2 评测指标:成功率、效率、成本、安全违规率

建议一个 Agent 评测集至少包含 50 到 200 个任务,覆盖正常输入、边缘输入和恶意输入三类。每一轮评测记录以下指标:

指标含义计算方式关注原因
任务成功率Agent 在允许步数内完成目标的比例完成数 / 总任务数衡量最核心的执行能力
人工介入率需要人工确认或修正的比例介入任务数 / 总任务数反映 Agent 独立性和稳定性
平均调用工具次数完成任务平均消耗的步骤数总工具调用次数 / 完成数步骤越多,失败概率越高
Token 消耗完成任务的模型 Token 成本每次运行累加影响上线后的运营成本
安全违规率调用未授权工具或输出敏感信息的比例违规次数 / 总任务数决定能否进入生产环境

评测结果要固定下来。每次修改提示词、框架或模型版本后,再跑同一批任务对比,才能判断改动是变好还是变坏。

3.3 用表格固定一轮基准评测

举一个可参考的评测输出格式:

任务类别任务数成功率人工介入率平均工具调用次数平均 Token 消耗安全违规率
订单查询4090%5%1.812800%
消息通知3083%15%2.415600%
多任务编排3070%30%4.223403.3%

表格里的数据是示意值,不是固定结论。关键是固定任务集和统计口径。没有评测基线,Agent 的“能力提升”就永远只能靠演示视频讲故事。

4. 导致 Agent 失控的四个工程原因

4.1 上下文窗口和记忆混淆

Agent 需要把历史工具调用结果保存在上下文里。步骤越多,上下文越长,模型越容易遗忘开头信息,或者把工具输出和用户问题混淆。例如先查询了订单 A,下一步查询订单 B,模型可能在总结时把两者混在一起。

缓解方式是把每轮工具结果结构化,并在后续系统提示中强调当前关键变量。不要把所有历史消息以同一权重塞给模型。必要时做记忆压缩,只保留与当前任务有关的摘要。

4.2 工具调用缺少参数校验

Agent 接口传入的参数来自模型生成,不可信。模型可能把字符串"2024-13-45"当作日期参数,也可能把订单号写得超出长度限制。如果后端直接使用这些参数执行 SQL 或命令,轻则任务失败,重则造成数据问题。

正确做法是在工具注册层统一做参数校验:

def safe_query_order(order_id: str) -> str: if not order_id.isdigit() or len(order_id) > 20: raise ValueError(f"非法的订单号格式: {order_id}") return query_order(order_id)

这里只做格式检查,真实项目还需要补充数据库访问控制、SQL 参数化、超时限制和调用频控。

4.3 奖励信号设计缺陷

如果使用强化学习或偏好优化训练 Agent,奖励信号如果只看“最终是否完成”,模型会走捷径,例如反复重试、直接编造工具结果、绕过限制条件。演示里看起来聪明,实际是找到了评测漏洞。

更稳妥的奖励设计要同时关注过程:工具调用是否合法、步骤是否冗余、是否过早放弃、是否在信息不足时做了假设。

4.4 没有兜底终止机制

Agent 一旦进入死循环,会持续调用工具、消耗 Token,甚至产生外部影响。最小 Agent 里用了max_steps限制,生产环境还需要额外机制:

  • 单次任务最大耗时上限。
  • 单任务最大 Token 预算。
  • 连续失败后的熔断。
  • 高风险工具必须人工审批。

没有这些兜底,Agent 在演示中“闯祸”只是视频效果,在生产中“闯祸”就是事故。

5. 从 vLLM 服务到 RAG 链路:一个常见启动问题的排查实录

5.1 问题现象:embedding 和 reranker 模型无法启动

Agent 常搭配 RAG 使用,而 RAG 链路里 embedding 模型负责把文档向量化,reranker 模型负责对检索结果重排。很多团队使用 vLLM 同时部署 LLM、Embedding 和 Reranker,结果发现 LLM 能正常启动,但 embedding 和 reranker 启动失败。

常见报错类似:

ValueError: The model architecture 'BertModel' is not supported by vLLM.

或者:

TypeError: Arguments not supported for this model class: 'sentence_embedding'

5.2 根因分析:vLLM 对模型结构的支持边界

vLLM 主要针对生成式大模型的推理场景做了优化,它的算子调度、KV Cache、连续批处理都围绕自回归解码设计。Embedding 模型和 Reranker 模型很多基于 BERT/DeBERTa 等双向编码器架构,与 vLLM 当前支持的解码器架构并不一致。因此,即使模型文件能被加载,也可能在初始化阶段因模型结构不支持而报错。

这并不是说 vLLM 不能跑这类模型,而是提示我们在架构选型时,要区分“生成式推理引擎”和“向量/排序推理服务”。它们解决的是两类不同问题。

5.3 排查步骤和替代方案

遇到 vLLM 启动 embedding 模型失败时,按以下顺序排查:

  1. 查看启动日志中是否出现模型架构名称,例如BertModelRobertaModel
  2. 对照 vLLM 文档确认该架构是否在支持列表中。
  3. 如果当前 vLLM 版本不支持,再确认最新版本是否增加了对应支持。
  4. 如果最新版本仍然不支持,不要把时间耗在强行适配上,直接选择专用推理服务。

适合 embedding/reranker 的替代方案包括:

方案适用场景说明
sentence-transformers本地快速验证使用简单,适合中小批量离线向量化
FlagEmbedding中文检索和重排对中文语义向量支持较好
TEI文本嵌入推理服务Hugging Face 开源的嵌入推理服务
Xinference多模型统一管理同时支持 LLM、Embedding、Reranker

生产环境建议把生成式模型、Embedding 模型、Reranker 模型拆到不同服务,避免单一服务故障影响整个 Agent 链路。同时要增加健康检查和模型预热机制,否则 Agent 第一次请求会因为冷启动超时而导致整体失败。

6. Agent 上生产前的安全检查清单

6.1 工具权限最小化

给 Agent 暴露的每个工具都应该先问一句:必须由 Agent 直接调用吗?能改成只读查询吗?能加审批吗?例如“发送短信”工具和“查询用户信息”工具,风险等级完全不同。建议把所有工具按风险分级:

风险等级示例控制策略
低风险查询天气、解析日期直接调用
中风险读取业务库、修改缓存参数白名单 + 频控
高风险发送消息、执行订单、删除数据人工审批 + 操作审计

6.2 人工审批与变更回滚

高风险动作必须保留人工确认环节。不要把审批做成流程负担,而是把审批对象从“每次调用”升级为“每个动作类别”。例如第一次批准后,允许同类操作在一定时间内自动执行,超时或参数变化则需要重新审批。

同时,任何具有副作用的工具都要支持回滚。Agent 调用支付接口和取消接口要成对设计。日志里记录完整调用链,才能在出现问题时定位责任。

6.3 日志、监控和告警

Agent 的日志和普通接口日志不同,需要记录三个维度的信息:

  • 用户输入原文。
  • Agent 每一步的思考和工具调用结果。
  • Token 消耗和耗时。

监控指标至少要包含任务成功率、人工介入率、工具调用失败率、token 消耗和 Agent 运行时长。一旦指标偏离基线,立即告警并暂停高风险工具的自动执行权限。

6.4 一份可复用的发布检查清单

在发布新 Agent 或升级模型前,对照以下清单逐项确认:

  • [ ] 评测集是否覆盖正常、边缘、恶意三类输入。
  • [ ] 工具注册表是否存在未使用的危险函数。
  • [ ] 每个工具的输入参数是否做了格式校验。
  • [ ] 高风险工具是否有人工审批入口。
  • [ ] 最大步数、超时、Token 预算是否配置。
  • [ ] 日志是否完整记录用户输入、工具调用、模型输出。
  • [ ] 是否配置失败告警和自动熔断。
  • [ ] 是否定义了回滚或补偿操作。

这份清单不只是给 Agent 的,也是给所有接入 Agent 的业务系统的。Agent 项目的核心风险往往不在模型本身,而在外部工具和权限链路。

7. 怎么从营销剧本里读出真正的技术信息

7.1 拆解演示视频的关键步骤

看到一段 Agent 演示视频,不要急着转发,先试着回答以下问题:

  • 演示用了多少个任务?其中多少是一次成功的?
  • Agent 是否需要调用外部系统?外部系统是 mock 的吗?
  • 视频中有人工输入指令吗?还是模型自主完成?
  • Agent 如果走错一步,是否会立即停止?

一个演示视频能表达的信息其实很多,但要靠拆解才能得到。营销剧本会刻意省略失败和限制,而工程判断需要主动补全这些信息。

7.2 建立自己的评测集

最有效的防营销手段是自己跑评测。建议从三个来源建立评测集:

  1. 收集用户真实需求,去重后做成任务。
  2. 从公开数据集里抽取 Agent 相关任务。
  3. 根据业务风险点设计对抗样本,例如让 Agent 尝试调用未授权工具。

评测集不需要一开始就很大。先做 20 个任务,跑通统计流程,再逐步扩展到 200 个。关键是评测集要固定、可回归、能对比版本差异。

7.3 下一步学习路线

Agent 开发的学习路径可以从这几个方向展开:

  • 熟悉大模型 API 的 tool calling 参数。
  • 理解 Embedding 和 Reranker 在 RAG 链路中的位置。
  • 练习把一个普通 API 封装成安全可控的工具。
  • 学习 LangChain、LlamaIndex 等框架的调度机制。
  • 研究评测、安全、监控等工程化能力。

真正值得投入时间的,不是追逐“哪个 Agent 更能闯祸”,而是把 Agent 的每一次工具调用、每一个决策流程变成可观测、可控制、可评测的系统工程。营销剧本负责吸引眼球,工程实践负责让 Agent 真正稳定地完成任务。对开发者来说,后者才是长期价值所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询