公司动态
从演示到落地:构建安全可控的AI Agent工程实践
近一年的 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 之前建议先回答三个问题Agent 允许调用哪些工具每个工具的调用条件是什么Agent 需要多少步骤才能完成任务最大步数是多少Agent 失败后应该重试、求助还是终止由谁决定这三个问题定义清楚后面所有代码和评测都有依据。营销剧本不会告诉你这些约束但真实项目必须自己设计。2. 从零构建一个“能干活但不会闯祸”的最小 Agent2.1 选型先定推理入口再选 Agent 框架构建 Agent 的第一步不是选框架而是选定模型推理入口。常见的推理入口有两类云端模型 API 和本地推理服务。云端 API 集成快但需要把业务数据传给外部服务本地推理可以用 vLLM、Ollama、LM Studio 等工具部署数据可控但环境配置成本更高。选型时建议按这个顺序判断数据是否允许出内网。请求延迟和吞吐要求。是否依赖 Embedding、Reranker 等辅助模型。团队是否具备 GPU 运维能力。如果选择云端 API后续例子可以直接调用 OpenAI、Anthropic、百炼等服务的兼容接口。如果选择本地推理需要额外处理模型格式、张量并行、显存占用、推理服务与 Agent 框架之间的协议兼容。2.2 项目结构和依赖准备下面以一个最小可运行案例为例。项目不需要复杂框架只使用 Python 标准库加一个模型调用函数便于理解 Agent 的“规划-调用-观察-终止”循环。实际项目建议使用 LangChain、LlamaIndex 或自研调度层但核心思路相同。目录结构agent_demo/ ├── main.py ├── requirements.txt └── tools/ └── registries.pyrequirements.txt 示例openai1.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_asciiFalse)再实现主循环。模型先决定调不调用工具如果调用工具则返回结构化工具调用信息。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( modelgpt-4o-mini, messagesmessages, 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(fAgent 尝试调用未注册工具: {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_steps3能避免 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_embedding5.2 根因分析vLLM 对模型结构的支持边界vLLM 主要针对生成式大模型的推理场景做了优化它的算子调度、KV Cache、连续批处理都围绕自回归解码设计。Embedding 模型和 Reranker 模型很多基于 BERT/DeBERTa 等双向编码器架构与 vLLM 当前支持的解码器架构并不一致。因此即使模型文件能被加载也可能在初始化阶段因模型结构不支持而报错。这并不是说 vLLM 不能跑这类模型而是提示我们在架构选型时要区分“生成式推理引擎”和“向量/排序推理服务”。它们解决的是两类不同问题。5.3 排查步骤和替代方案遇到 vLLM 启动 embedding 模型失败时按以下顺序排查查看启动日志中是否出现模型架构名称例如BertModel、RobertaModel。对照 vLLM 文档确认该架构是否在支持列表中。如果当前 vLLM 版本不支持再确认最新版本是否增加了对应支持。如果最新版本仍然不支持不要把时间耗在强行适配上直接选择专用推理服务。适合 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 建立自己的评测集最有效的防营销手段是自己跑评测。建议从三个来源建立评测集收集用户真实需求去重后做成任务。从公开数据集里抽取 Agent 相关任务。根据业务风险点设计对抗样本例如让 Agent 尝试调用未授权工具。评测集不需要一开始就很大。先做 20 个任务跑通统计流程再逐步扩展到 200 个。关键是评测集要固定、可回归、能对比版本差异。7.3 下一步学习路线Agent 开发的学习路径可以从这几个方向展开熟悉大模型 API 的 tool calling 参数。理解 Embedding 和 Reranker 在 RAG 链路中的位置。练习把一个普通 API 封装成安全可控的工具。学习 LangChain、LlamaIndex 等框架的调度机制。研究评测、安全、监控等工程化能力。真正值得投入时间的不是追逐“哪个 Agent 更能闯祸”而是把 Agent 的每一次工具调用、每一个决策流程变成可观测、可控制、可评测的系统工程。营销剧本负责吸引眼球工程实践负责让 Agent 真正稳定地完成任务。对开发者来说后者才是长期价值所在。