
1. 为什么“隔离内网”是 AI Agent 工程化的分水岭很多人第一次听到“隔离内网下跑 AI Agent”脑子里冒出来的第一个念头是不就是把模型部署到一台没有外网的机器上吗能有多难我一开始也这么想直到真正在一个物理隔离的机房里折腾了两周才发现这件事的复杂度根本不在模型本身而在于整条工具链的“断网适配”。所谓隔离内网通常指与公网完全物理断开、或者仅通过单向通道做数据摆渡的局域网环境。这种环境在金融、制造、能源、科研等行业的内部系统中非常普遍。它的核心特征是没有外网 DNS、没有公网 IP、无法直接访问任何云端 API、包管理器的默认源不可达、容器镜像需要离线导入。你平时在公网环境下习以为常的pip install、npm install、docker pull在这里全部失效。那为什么还要在这种环境里跑 AI Agent原因很直接数据不能出去。很多业务场景下的文档、代码、工单、日志天然带有敏感性不可能送到公网大模型去推理。但业务方又确实需要 Agent 的自动化能力——比如自动分类工单、自动生成测试用例、自动检索内部知识库、自动执行运维巡检脚本。这就形成了一个矛盾能力要数据不能出。解决这个矛盾的唯一路径就是把 AI Agent 的整套运行时搬到内网里。这包括模型推理服务、Agent 编排框架、工具调用协议、技能插件、向量数据库、以及最容易被忽视的——依赖包和镜像的离线供给。我踩过的第一个大坑就在这里。当时我以为只要把模型权重拷进去、装个推理框架就完事了结果发现 Agent 框架依赖的某个 Python 包又依赖了一个需要编译的 C 扩展而那个 C 扩展的源码里又引用了外网才能拉取的子模块。一个看似简单的依赖卡了整整一天。所以这篇文章不是讲“怎么调模型参数”而是讲“怎么在断网环境下把一套 AI Agent 工程真正跑起来并且稳定运行”。适合的读者是需要在隔离环境中落地 Agent 的后端工程师、运维工程师、以及负责内网平台建设的技术负责人。如果你只是想在公网环境玩一玩 Agent这篇内容可能对你偏重了但如果你面对的是“机器不能出网”的硬约束那接下来的内容应该能帮你少走不少弯路。2. 断网环境下的依赖供给从“缺什么装什么”到“提前备好一切”2.1 离线包仓库的搭建逻辑与常见误区在公网环境下依赖管理是“按需拉取”在内网环境下依赖管理必须变成“提前备货”。这个思维转变听起来简单但实际操作中有很多细节容易翻车。最直接的做法是在一台有外网的机器上把所有需要的 Python 包、Node 包、系统依赖全部下载下来然后通过物理介质导入内网。但问题在于你怎么知道“所有需要的包”到底是哪些我的做法是分三层来准备。第一层是直接依赖就是你在requirements.txt或package.json里明确写出来的包。第二层是传递依赖也就是这些包自己依赖的包。第三层是运行时动态依赖这类最隐蔽——有些包在特定代码路径下才会 import 某个模块静态分析根本看不出来。对于第一层和第二层可以用pip download配合--platform和--python-version参数做交叉下载。比如目标内网机器是 Linux x86_64 Python 3.11那就在外网机器上执行pip download -r requirements.txt \ --platform manylinux2014_x86_64 \ --python-version 311 \ --only-binary:all: \ -d ./offline_packages这里有几个关键点。--platform必须和目标机器的架构完全匹配否则下载下来的 wheel 包装不上。--only-binary:all:强制只下载二进制包避免下载到需要现场编译的源码包——在内网里编译是最容易出问题的环节因为编译工具链本身可能就不全。如果某个包没有对应平台的 wheel那就需要单独处理要么找替代包要么在外网机器上编译好再打包。对于第三层动态依赖我的经验是不要试图一次性分析清楚而是采用“迭代补齐”的策略。先在内网跑一遍完整的业务流程把报错信息收集起来每遇到一个ModuleNotFoundError就补一个包。通常跑三轮下来依赖就基本齐了。注意pip download下载的包默认包含所有依赖但如果你的requirements.txt里有些包是通过-e方式安装的本地包这些不会被自动打包需要手动处理。2.2 容器镜像的离线搬运与层复用技巧如果 Agent 是跑在容器里的那镜像的离线搬运就是另一个必须解决的问题。docker pull在内网里是走不通的需要用docker save和docker load做中转。基本操作很直接# 外网机器上保存镜像 docker save my-agent-image:latest -o agent-image.tar # 内网机器上加载镜像 docker load -i agent-image.tar但这里有个很容易被忽视的问题镜像体积。一个包含 PyTorch、CUDA 运行时、以及各种 Python 依赖的镜像动辄好几个 GB。如果每次更新都要完整搬运效率极低。我的优化思路是分层搬运。把镜像拆成基础层和业务层基础层包含操作系统、CUDA、Python 运行时、以及不常变动的核心依赖业务层只包含 Agent 代码和经常变动的配置。基础层一次搬运到位后续只更新业务层。这样每次搬运的体积可以从几个 GB 降到几十 MB。具体做法是写一个多阶段构建的 Dockerfile把基础环境固化成一个单独的镜像业务镜像FROM那个基础镜像。更新时只需要重新构建业务层然后docker save业务镜像即可。不过要注意docker save默认会保存所有层如果基础层没有变化可以用--platform参数配合镜像分层工具做增量导出但这需要额外的工具支持在内网环境里不一定方便。更简单的做法是维护一个内网的镜像仓库比如用 registry 的离线部署方案把基础镜像推送到内网 registry业务镜像只推送变更层。2.3 模型权重的分片传输与校验模型权重的搬运是另一个大工程。一个 7B 参数的模型FP16 精度下大约 14GB如果是 70B 模型那就直接上百 GB 了。这么大的文件通过物理介质搬运时分片和校验是必须的。分片用split命令就可以split -b 2G model_weights.bin model_part_每个分片 2GB方便拷贝到 U 盘或移动硬盘。到了内网之后用cat合并cat model_part_* model_weights.bin合并之后必须做校验。用sha256sum生成校验值在外网机器上算一次内网合并后再算一次对比一致才能用。我遇到过好几次因为拷贝过程中断导致文件损坏的情况如果没有校验后面加载模型时报的错会非常莫名其妙排查起来很浪费时间。另外如果模型是从 Hugging Face 下载的注意它的权重文件通常是分片的比如pytorch_model-00001-of-00003.bin而且有一个pytorch_model.bin.index.json索引文件。搬运时要把整个目录结构保持完整不能只拷权重文件而漏掉配置文件和 tokenizer 文件。3. MCP 协议在内网 Agent 中的角色与落地方式3.1 MCP 到底解决了什么问题MCP 是 Model Context Protocol 的缩写它要解决的核心问题是让 Agent 用一种标准化的方式和外部工具、数据源交互。在没有 MCP 之前如果你想让 Agent 调用一个内部 API、查询一个数据库、或者读取一个文件系统通常的做法是在 Agent 代码里硬编码这些调用逻辑。每接一个新工具就要改一次 Agent 的代码耦合度很高。而且不同 Agent 框架之间的工具定义方式还不一样换一个框架就要重写一遍。MCP 的思路是把这个交互抽象成一个协议层。工具提供方实现一个 MCP Server把能力暴露出来Agent 作为 MCP Client通过标准协议去发现和调用这些能力。这样 Agent 和工具之间就解耦了工具可以独立开发、独立部署、独立更新。在内网环境里MCP 的价值更加突出。因为内网的工具生态往往是碎片化的——可能有几个不同的内部系统、几套不同的数据库、一些遗留的脚本工具。如果每个都要在 Agent 里单独适配维护成本会非常高。用 MCP 统一起来之后Agent 只需要对接 MCP 协议具体的工具实现由各个团队自己维护。3.2 内网 MCP Server 的部署要点在内网部署 MCP Server首先要确定通信方式。MCP 支持两种主要的传输方式stdio标准输入输出和 SSEServer-Sent Events。stdio 方式适合本地进程间通信Agent 和 MCP Server 在同一台机器上SSE 方式适合跨机器通信MCP Server 作为独立的 HTTP 服务运行。在内网环境里如果 Agent 和工具在同一台机器上stdio 是最简单的方式不需要考虑网络配置和端口开放。但如果工具分布在不同的机器上那就需要 SSE 方式这时候要注意几个问题。第一是服务发现。内网没有公网 DNSMCP Server 的地址需要手动配置或者通过内网的配置中心下发。我的做法是在 Agent 的配置文件里维护一个 MCP Server 列表每个 Server 标注名称、地址、端口、以及能力描述。Agent 启动时读取这个列表逐个建立连接。第二是认证与鉴权。内网虽然相对封闭但也不能完全不设防。MCP Server 应该至少有一个简单的 token 认证机制防止误调用。可以在 HTTP header 里加一个预共享的 tokenAgent 和 Server 双方约定好即可。第三是超时与重试。内网环境虽然延迟低但服务稳定性不一定比公网好尤其是那些跑在老旧的内部系统上的工具。MCP Client 端要设置合理的超时时间并且对可重试的错误做退避重试。我的经验是超时设 30 秒比较合适太短容易误判太长会拖慢整个 Agent 的响应。3.3 用 MCP 封装内部工具的实操示例假设内网有一个工单系统提供 REST API 可以查询和创建工单。我们要把它封装成一个 MCP Server让 Agent 能够调用。首先定义一个 MCP Server 的基本结构。用 Python 实现的话可以用mcp这个库from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app Server(ticket-system) app.list_tools() async def list_tools(): return [ Tool( namequery_ticket, description根据工单ID查询工单详情, inputSchema{ type: object, properties: { ticket_id: {type: string, description: 工单编号} }, required: [ticket_id] } ), Tool( namecreate_ticket, description创建一条新工单, inputSchema{ type: object, properties: { title: {type: string}, description: {type: string}, priority: {type: string, enum: [low, medium, high]} }, required: [title, description] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict): if name query_ticket: result await query_ticket_api(arguments[ticket_id]) return [TextContent(typetext, textresult)] elif name create_ticket: result await create_ticket_api( arguments[title], arguments[description], arguments.get(priority, medium) ) return [TextContent(typetext, textresult)] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options())这个例子里list_tools定义了 Agent 可以发现的两个工具call_tool负责实际执行。query_ticket_api和create_ticket_api是内部实现的函数负责调用工单系统的 REST API。部署到内网时把这个脚本和它的依赖打包放到目标机器上然后在 Agent 的配置里注册这个 MCP Server 的启动命令即可。如果是 SSE 方式把stdio_server换成 SSE 的 server 实现并指定监听端口。提示MCP Server 的工具描述description写得越清晰Agent 选择工具的准确率越高。不要写“查询工单”这种模糊描述要写“根据工单ID查询工单的详细信息包括状态、处理人、创建时间”。Agent 是靠这些描述来做决策的。4. Skills 机制让 Agent 在内网里“有技能可用”4.1 Skills 与 MCP 的分工边界Skills 和 MCP 经常被混为一谈但它们在 Agent 工程里扮演的角色其实不同。MCP 解决的是“Agent 怎么调用外部能力”的协议问题Skills 解决的是“Agent 在什么场景下该用什么能力”的知识问题。打个比方MCP 像是给 Agent 装了一双手让它能够操作各种工具Skills 像是给 Agent 配了一本操作手册告诉它遇到什么情况该用哪双手、怎么用。在内网环境里Skills 的重要性体现在内网的业务逻辑往往有很强的领域特殊性通用的 Agent 能力不足以覆盖。比如一个内部的运维 Agent它需要知道“当磁盘使用率超过 85% 时应该先检查日志目录再清理临时文件”这种知识不是模型预训练里有的必须通过 Skills 注入。Skills 的典型形式是一组结构化的指令文档包含触发条件、执行步骤、注意事项。Agent 在运行时根据当前上下文匹配相关的 Skill然后按照 Skill 里的步骤来执行。4.2 内网 Skills 的编写规范与组织方式写 Skill 不是写文档它的读者是 Agent所以格式和措辞都要针对 Agent 的理解方式来优化。一个 Skill 通常包含这几个部分名称、触发条件、前置检查、执行步骤、异常处理、输出格式。触发条件要写得具体比如“当用户提到‘工单积压’或‘工单超时’时触发”而不是“当用户需要工单帮助时触发”。执行步骤要写成有序列表每一步都是一个明确的动作最好能对应到具体的 MCP 工具调用。我习惯把 Skills 组织成一个目录结构每个 Skill 一个 Markdown 文件文件名就是 Skill 的名称。目录里再放一个索引文件列出所有 Skill 的名称和一句话描述Agent 启动时先读索引需要时再加载具体的 Skill 文件。# Skill: 工单积压排查 ## 触发条件 - 用户提到工单积压、工单超时、待处理工单过多 - 定时任务检测到待处理工单数超过阈值 ## 前置检查 - 确认当前用户有工单系统的查询权限 - 确认工单系统的 MCP Server 连接正常 ## 执行步骤 1. 调用 query_ticket_stats 获取当前各状态工单数量 2. 如果待处理工单数超过 50调用 query_ticket_list 获取最早的 20 条工单 3. 分析这些工单的优先级和创建时间识别是否有高优先级工单被积压 4. 调用 generate_report 生成积压分析报告 5. 如果存在高优先级积压调用 notify 发送告警 ## 异常处理 - 如果 query_ticket_stats 超时重试一次仍失败则报告工单系统暂时不可用 - 如果工单数量为 0直接返回当前无积压工单 ## 输出格式 - 积压总数 - 高优先级积压数 - 最早的 5 条工单摘要 - 建议的处理动作这种结构化的写法Agent 解析起来很顺畅。关键是每一步都要有明确的工具调用对应不要让 Agent 去“自由发挥”。4.3 Skills 的版本管理与灰度更新在内网环境里Skills 的更新不像公网那么方便不能随时改随时生效。所以需要一套版本管理机制。我的做法是给每个 Skill 文件加一个版本号放在文件头部的元数据里。Agent 加载 Skill 时记录版本号执行时在日志里带上版本信息。这样出问题的时候可以追溯到具体是哪个版本的 Skill 导致的。灰度更新方面可以在 Agent 的配置里加一个 Skill 白名单或黑名单。新版本的 Skill 先只对部分 Agent 实例生效观察一段时间没问题再全量推送。内网环境里没有公网的 A/B 测试基础设施这种手动灰度虽然原始但足够可靠。另外要注意的是Skills 的更新往往需要和 MCP Server 的更新配合。比如 Skill 里新增了一个工具调用步骤但对应的 MCP Server 还没部署新版本那 Agent 执行到那一步就会失败。所以我的经验是先更新 MCP Server确认新工具可用再更新 Skill。顺序反了就容易出问题。5. 并发压力下的 Agent 工程调优5.1 内网 Agent 的并发瓶颈到底在哪“AI Agent 怎么扛并发”是最近被问得很多的问题。在公网环境里瓶颈通常在模型 API 的速率限制但在内网环境里瓶颈的分布完全不同。我实测下来内网 Agent 的并发瓶颈通常出现在三个地方模型推理服务的吞吐、MCP 工具调用的响应时间、Agent 编排框架本身的调度效率。模型推理服务方面如果用的是本地部署的开源模型并发能力取决于 GPU 显存和推理框架的批处理策略。一个 7B 模型在单张消费级显卡上如果用 vLLM 这类支持连续批处理的框架大概能支撑 10-20 路并发如果用朴素的 Hugging Face pipeline可能 2-3 路就撑不住了。MCP 工具调用方面如果工具背后是内部的老旧系统响应时间可能很不稳定。一个查询请求正常 200ms 返回但遇到数据库锁表可能变成 5 秒。这种长尾延迟会拖垮整个 Agent 的并发能力因为 Agent 的执行线程会被阻塞。Agent 编排框架方面很多框架默认是同步执行的一个请求处理完才处理下一个。要支持并发必须改成异步或者多线程模式。5.2 用异步编排提升 Agent 吞吐解决并发问题的第一步是把 Agent 的执行链路改成全异步。这包括模型调用异步、MCP 工具调用异步、以及 Agent 主循环异步。以 Python 为例如果 Agent 框架本身不支持异步可以用asyncio包一层。核心思路是把每个请求的处理逻辑写成一个协程然后用asyncio.gather或者信号量来控制并发数。import asyncio semaphore asyncio.Semaphore(10) # 最大并发数 async def handle_request(request): async with semaphore: # 模型推理 model_result await call_model_async(request.prompt) # 工具调用 tool_result await call_mcp_tool_async(model_result.tool_name, model_result.args) # 生成最终回复 final await call_model_async(build_final_prompt(tool_result)) return final async def main(): requests [handle_request(r) for r in incoming_requests] results await asyncio.gather(*requests) return results这里的semaphore是关键它限制了同时进行的请求数防止把后端服务打垮。具体设多少要根据模型推理服务和 MCP 工具的实际承载能力来定。我的经验是从 5 开始试逐步往上加观察响应时间和错误率找到拐点。5.3 MCP 工具调用的超时与降级策略MCP 工具调用是并发链路里最不可控的一环。内网系统的稳定性参差不齐必须做好超时和降级。超时设置上我建议分两级软超时和硬超时。软超时比如 10 秒到了之后 Agent 先给用户返回一个“正在处理中”的中间状态同时继续等待硬超时比如 30 秒到了之后直接放弃这次工具调用走降级逻辑。降级逻辑要根据业务场景来设计。比如查询类工具超时了可以返回缓存的历史数据创建类工具超时了可以先把请求写入本地队列稍后重试。最忌讳的是超时之后直接报错让用户看到一个失败的结果。async def call_mcp_tool_with_fallback(tool_name, args): try: result await asyncio.wait_for( call_mcp_tool_async(tool_name, args), timeout30 ) return result except asyncio.TimeoutError: # 降级查缓存 cached get_from_cache(tool_name, args) if cached: return cached # 降级写入重试队列 enqueue_retry(tool_name, args) return 请求已提交正在后台处理请稍后查看结果这个模式我在实际项目里用了很久效果很稳。用户不会因为后端系统的偶发慢响应而看到报错体验上好了很多。5.4 推理服务的批处理与显存优化如果内网 Agent 用的是本地部署的模型推理服务的优化空间很大。核心思路是批处理把多个请求攒在一起一次性送给 GPU 推理这样能显著提升吞吐。vLLM 是这方面比较成熟的方案它支持连续批处理continuous batching能在请求不断到来的情况下动态组批。部署方式也很简单python -m vllm.entrypoints.openai.api_server \ --model /path/to/model \ --tensor-parallel-size 1 \ --max-num-seqs 32 \ --gpu-memory-utilization 0.9max-num-seqs控制同时处理的最大请求数gpu-memory-utilization控制显存使用率。这两个参数需要根据显卡型号和模型大小来调。我的经验是先把gpu-memory-utilization设到 0.9然后逐步增加max-num-seqs直到出现显存不足或者响应时间明显上升为止。如果显存实在不够可以考虑量化。4-bit 量化能把显存占用降到 FP16 的四分之一左右代价是推理质量会有一定下降。在内网环境里如果业务对精度要求不是极高量化是很划算的选择。6. 内网 Agent 的可观测性与故障排查6.1 日志体系的设计让问题可追溯内网环境里没有公网的监控 SaaS可观测性全靠自己搭。日志是最基础也是最重要的一环。Agent 的日志要覆盖这几个层面请求级日志每个用户请求的完整生命周期、模型调用日志每次推理的输入输出和耗时、工具调用日志每次 MCP 调用的参数、结果、耗时、错误日志异常堆栈和上下文。我习惯用结构化日志每条日志是一个 JSON 对象包含timestamp、level、trace_id、component、message、extra字段。trace_id贯穿一个请求的所有日志方便串联。import logging import json import uuid class StructuredLogger: def __init__(self, component): self.component component self.logger logging.getLogger(component) def log(self, level, message, trace_idNone, **extra): record { timestamp: datetime.utcnow().isoformat(), level: level, component: self.component, trace_id: trace_id or str(uuid.uuid4()), message: message, extra: extra } self.logger.info(json.dumps(record, ensure_asciiFalse))日志输出到文件之后可以用jq或者简单的 Python 脚本做查询和分析。内网环境里不一定有 ELK 这样的日志平台但至少要做到能按trace_id把一次请求的完整链路捞出来。6.2 常见故障的排查链路内网 Agent 的故障排查我总结了一个从外到内的排查顺序。第一步确认 Agent 进程是否存活。这个看似简单但内网环境里进程被 OOM Killer 杀掉、或者因为某个未捕获异常而退出都是常见情况。用systemctl status或者supervisorctl status先看一眼。第二步确认模型推理服务是否可达。用curl直接打推理服务的健康检查接口看是否返回正常。如果推理服务挂了Agent 的所有请求都会失败。第三步确认 MCP Server 是否可达。逐个检查 Agent 配置里注册的 MCP Server看连接是否正常。可以用 MCP 协议自带的 ping 机制或者直接发一个简单的工具调用请求测试。第四步看日志里的错误模式。如果前面三步都正常那问题可能在业务逻辑层面。按trace_id捞一次失败请求的完整日志看是在哪一步出的错。第五步复现问题。如果日志信息不够就手动构造一个相同的请求在测试环境里复现。内网环境里复现问题通常比公网容易因为环境是可控的。我遇到过最隐蔽的一个问题是MCP Server 在长时间运行后文件描述符泄漏导致新的连接建立失败。日志里只看到“连接超时”看不出根因。后来用lsof检查进程的文件描述符数量才发现已经接近上限。解决办法是在 MCP Server 里加一个定时重启机制或者修复泄漏的代码。6.3 性能基线与容量规划在内网环境里做容量规划不能靠拍脑袋要有性能基线。性能基线包括单请求的平均响应时间、P95 响应时间、最大并发数、以及在不同并发下的错误率。这些数据要通过压测来获取。压测工具可以用locust或者简单的asyncio脚本。关键是压测的请求要模拟真实场景不能只发同一种请求。我的做法是从生产日志里采样一批真实请求脱敏后作为压测输入。拿到基线数据之后容量规划就简单了如果业务预期峰值 QPS 是 10而单实例在 P95 响应时间可接受的前提下能扛 5 QPS那就部署 2-3 个实例留一定余量。注意内网环境里的扩容往往比公网慢因为涉及机器申请、网络配置、镜像分发等流程。所以容量规划要留足够的 buffer不能卡着上限来。7. 一些踩坑之后的经验沉淀7.1 依赖版本锁定比你想的重要在内网环境里依赖版本漂移是噩梦。因为不能随时从外网拉最新版本一旦某个依赖升级引入了不兼容的变更回滚成本很高。我的做法是所有依赖必须锁定到具体版本包括传递依赖。Python 用pip freeze生成完整的requirements.txtNode 用package-lock.json系统包用apt-mark showmanual记录。每次更新依赖时在外网环境完整测试通过后再整体打包搬运到内网。7.2 给 Agent 设一个“最大执行步数”Agent 在执行复杂任务时有可能陷入循环——反复调用同一个工具、或者在不同工具之间来回跳转。在内网环境里这种循环会白白消耗推理资源和工具调用配额。我的做法是在 Agent 的编排逻辑里加一个最大步数限制比如 20 步。超过之后强制终止返回当前已完成的部分结果并记录一条警告日志。这个限制可以根据业务复杂度调整但一定要有。7.3 模型输出的格式约束要前置内网 Agent 经常需要调用内部工具而工具对输入参数的格式往往有严格要求。如果让模型自由生成参数很容易出现格式错误。解决办法是在 Prompt 里前置格式约束并且在模型输出之后加一层校验。校验不通过就重新生成或者用规则做修正。比如要求模型输出 JSON那就用 JSON Schema 做校验要求某个字段是枚举值那就检查是否在枚举范围内。7.4 内网时间同步问题容易被忽视这个问题很隐蔽内网机器的时间如果没有同步Agent 日志的时间戳就会错乱排查问题时根本对不上。更严重的是如果 MCP 协议或者认证机制里涉及时间戳校验时间偏差会导致认证失败。确保内网所有机器都配置了 NTP 同步指向内网的时间服务器。如果没有内网时间服务器至少手动定期校准。这个小事不注意后面会带来很多莫名其妙的故障。7.5 留一条“逃生通道”最后分享一个我觉得最重要的经验在内网 Agent 的部署里一定要留一条不依赖 Agent 的逃生通道。什么意思就是当 Agent 完全不可用的时候业务方还能通过其他方式完成关键操作。比如提供一个简单的 Web 表单或者命令行工具让用户可以手动触发那些 Agent 负责的任务。这条通道平时不用但在 Agent 出故障时能保证业务不中断。我在一个项目里就是因为没留这条通道Agent 的 MCP Server 出问题之后整个工单处理流程卡住了半天业务方意见很大。后来补了一个简单的备用入口虽然功能简陋但至少保证了业务连续性。内网环境里的系统稳定性永远比先进性重要。Agent 再智能也得先保证“能用”。