
1. 这不是“把模型搬进内网”——而是重构整个AI Agent交付链路“隔离内网下 AI Agent 工程实战”这个标题第一眼容易被理解成“找个GPU服务器把ChatGLM或Qwen模型docker run起来再套个FastAPI接口”。我去年在三个不同行业的客户现场都见过这种做法开发团队花两周搭好环境演示时模型能回答“今天天气怎么样”但一到真实业务场景就卡死——调用内部ERP系统超时、处理Excel附件内存溢出、多用户并发时LLM推理队列堆满、日志里全是ConnectionRefusedError: [Errno 111] Connection refused。问题不在模型本身而在于我们把互联网时代的AI工程范式原封不动地塞进了物理隔离的内网环境里。内网不是“没网的公网”它是另一套基础设施语言。公网依赖DNS解析、CDN加速、云服务API网关、弹性伸缩集群而典型隔离内网只有静态IP段、无外网DNS、无证书颁发机构、无统一身份认证中心、甚至没有NTP时间同步服务。你不能指望LangChain的RequestsWrapper自动处理内网HTTP代理配置也不能让LangGraph的StateGraph凭空感知到本地部署的Oracle数据库监听端口是否真的在1521上跑着。更关键的是所有AI Agent的核心价值——动态调用工具Tool、编排工作流Workflow、维护长期记忆Memory——在内网中全部变成需要手工缝合的“硬连接”。我接手的第一个内网AI项目是某省级电力调度中心的故障研判助手。他们要求Agent能读取SCADA系统实时数据、调阅历史检修工单PDF、生成符合《DL/T 860》规范的研判报告。表面看是典型的RAGFunction Calling但实际落地时发现SCADA数据只开放OPC UA协议访问且必须用特定证书双向认证工单PDF存储在国产信创文档库中SDK只提供Java版而我们的Agent主框架是Python报告模板需嵌入国密SM4加密的电子签章。这时候“部署一个LLM”已经退居二线真正的工程重心变成了如何让Python进程安全加载Java SDK如何在无外网环境下生成并验证SM4签名OPC UA客户端如何与LangChain的Tool抽象层对齐这些问题没有现成答案只能靠一行行代码去填平协议鸿沟。所以这篇实战记录不讲“怎么下载Qwen2-7B”也不教“LangGraph基础语法”。它聚焦于内网AI Agent特有的四重枷锁网络拓扑枷锁、协议兼容枷锁、安全策略枷锁、运维闭环枷锁。每一个枷锁背后都是真实生产环境中踩过的坑、验证过的方案、以及必须写死在部署手册里的参数。如果你正面临“领导说下周要上线AI助手但服务器连百度都ping不通”的处境接下来的内容就是你真正需要的施工图纸。2. 网络拓扑枷锁当“localhost”不再是默认选项内网环境最根本的错觉是认为所有服务都在同一台机器或同一子网。现实往往残酷得多核心数据库在A网段10.1.10.0/24文件存储在B网段10.1.20.0/24中间件集群在C网段10.1.30.0/24而你的AI Agent服务被强制部署在D网段10.1.40.0/24——四个网段之间仅允许特定端口白名单通信。这种设计源于等保三级要求却让Agent的Tool调用变成一场精密的网络交响乐。2.1 端口映射不是万能解药为什么frp/ngrok在内网失效搜索热词里高频出现“frp内网穿透”“ngrok教程”这恰恰暴露了认知偏差。frp/ngrok本质是反向代理隧道它需要一台有公网IP的中继服务器作为跳板。但在物理隔离内网中这台中继服务器根本不存在。更致命的是即使你自建了一台“伪中继”比如用DMZ区服务器做转发也会触发安全审计告警——因为所有跨网段流量必须经过防火墙策略审查而frp的TCP长连接会绕过策略引擎的深度包检测DPI。我实测过某金融客户环境他们在DMZ区部署frp serverAgent服务作为client连接。结果发现当Agent调用内部CRM系统的REST API时请求路径变成Agent → frp client → frp server → CRM。防火墙看到的是frp server发起的连接无法关联到原始Agent请求上下文导致基于URL路径的访问控制策略完全失效。最终被迫下线frp改用防火墙直接放行Agent到CRM的8080端口。提示内网穿透工具在隔离内网中唯一可行的场景是调试阶段临时打通开发机到测试环境。正式部署必须回归防火墙白名单机制这是安全合规的底线。2.2 DNS缺失下的服务寻址硬编码IP还是构建轻量服务发现公网环境依赖DNS将api.crm.internal解析为IP内网却常只有hosts文件或静态ARP表。LangChain的Tool定义通常写死URLclass CRMTool(BaseTool): name crm_query description Query customer data from CRM system def _run(self, query: str) - str: # 这里写死IP维护噩梦开始了 response requests.get(http://10.1.20.15:8080/api/v1/customers, params{q: query}) return response.json()当CRM系统因升级迁移到10.1.20.16时所有Agent实例都要重新部署。更糟的是如果CRM是双活集群硬编码IP会丢失负载均衡能力。我们的解决方案是在Agent进程内嵌轻量服务发现客户端不依赖外部注册中心如Consul而是读取本地配置文件# service-discovery.yaml services: crm: endpoints: - host: 10.1.20.15 port: 8080 weight: 50 - host: 10.1.20.16 port: 8080 weight: 50 health_check: /health scada: endpoints: - host: 10.1.10.100 port: 4840 # OPC UA default weight: 100Agent启动时加载此文件每次调用前执行健康检查HTTP GET或TCP connect按权重轮询可用节点。实测在电力调度中心当SCADA主节点宕机时Agent能在3秒内自动切换到备用节点比等待DNS TTL过期快两个数量级。2.3 协议栈撕裂HTTP/HTTPS之外的生存空间内网系统协议五花八门SCADA用OPC UA基于二进制TCPERP用SAP RFC专有协议文档库用WebDAVXML over HTTP工控设备用Modbus TCP纯二进制。LangChain默认只支持HTTP REST强行封装会导致性能灾难。以OPC UA为例标准Python库asyncua需要建立安全通道SecurityPolicy.Basic256Sha256而内网证书体系往往不兼容X.509标准。我们不得不定制化UA客户端from asyncua import Client from asyncua.crypto.security_policies import SecurityPolicyBasic256Sha256 class SecureOPCUAClient: def __init__(self, endpoint: str, cert_path: str, key_path: str): self.endpoint endpoint self.cert_path cert_path # 内网CA签发的PEM证书 self.key_path key_path async def read_node(self, node_id: str) - Any: client Client(self.endpoint) # 关键禁用证书链验证只校验公钥指纹 client.set_security( SecurityPolicyBasic256Sha256, self.cert_path, self.key_path, None, # 不传CA证书规避证书链验证 modeua.MessageSecurityMode.SignAndEncrypt ) await client.connect() try: node client.get_node(node_id) return await node.read_value() finally: await client.disconnect()这个None参数是血泪教训——内网CA证书未预装在系统信任库若强制验证会抛出ssl.SSLCertVerificationError。改为公钥指纹校验通过openssl x509 -in cert.pem -pubkey -noout | sha256sum预先计算并硬编码既保证通信安全又绕过证书链依赖。3. 协议兼容枷锁让AI Agent听懂内网系统的“方言”公网API遵循OpenAPI规范返回JSON错误码清晰内网系统则像一群说着不同方言的老人有的返回XML有的用SOAP包装有的只认固定长度二进制报文。AI Agent的Tool层必须成为“方言翻译官”而非简单HTTP客户端。3.1 XML地狱当CRM返回的是 soap:Envelope某政务客户CRM系统坚持使用SOAP 1.1请求必须是XML格式响应也是嵌套极深的XML。LangChain的requests_toolkit对此束手无策。我们构建了专用SOAP Toolimport xml.etree.ElementTree as ET from typing import Dict, Any class SOAPCRMTool(BaseTool): name crm_soap_query description Query CRM via SOAP protocol (legacy system) def _run(self, query_params: Dict[str, str]) - str: # 构造SOAP请求体严格遵循WSDL定义 soap_body f?xml version1.0 encodingutf-8? soap:Envelope xmlns:soaphttp://schemas.xmlsoap.org/soap/envelope/ soap:Body GetCustomerInfo xmlnshttp://tempuri.org/ customerID{query_params[id]}/customerID includeHistory{str(query_params.get(history, False)).lower()}/includeHistory /GetCustomerInfo /soap:Body /soap:Envelope headers { Content-Type: text/xml; charsetutf-8, SOAPAction: http://tempuri.org/GetCustomerInfo } response requests.post( http://10.1.20.15:8080/CrmService.asmx, datasoap_body, headersheaders, timeout30 ) # 解析SOAP响应提取GetCustomerInfoResponse内内容 root ET.fromstring(response.content) ns {soap: http://schemas.xmlsoap.org/soap/envelope/} body root.find(soap:Body, ns) result body.find(.//GetCustomerInfoResult, ns) return result.text if result is not None else No data # 在Agent中注册 agent_executor AgentExecutor( agentagent, tools[SOAPCRMTool()], verboseTrue )关键点在于WSDL定义必须人工解析并固化到代码中。自动生成SOAP客户端如zeep在内网常因缺少WSDL URL或SSL验证失败而崩溃手写XML模板反而稳定可靠。3.2 二进制协议Modbus TCP的字节序陷阱工控场景中Agent需读取PLC寄存器数据。Modbus TCP协议无JSON/XML只有12字节固定头功能码寄存器地址字节数。某次调试发现Agent返回的温度值总是负数——根源在于字节序endianness错误。PLC厂商文档写明“寄存器数据为Big-Endian”但Pythonstruct.unpack(H, data)解包后仍是负值。深入抓包发现实际传输的是IEEE 754单精度浮点数需用f而非Himport struct def read_modbus_float(ip: str, port: int, slave_id: int, address: int) - float: # 构造Modbus TCP请求简化版 transaction_id 0x0001 protocol_id 0x0000 length 0x0006 unit_id slave_id function_code 0x04 # Read Input Registers start_address address register_count 2 # Float需要2个16位寄存器 pdu struct.pack(BBHH, function_code, start_address 8, start_address 0xFF, register_count) adu struct.pack(HHHBB, transaction_id, protocol_id, length, unit_id, function_code) pdu[1:] with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((ip, port)) s.send(adu) resp s.recv(1024) # 解析响应跳过MBAP头7字节取寄存器数据4字节 data_bytes resp[9:13] # 2个寄存器 * 2字节 # 关键Big-Endian浮点数解包 return struct.unpack(f, data_bytes)[0] # Agent调用 temp read_modbus_float(10.1.10.200, 502, 1, 1000) # 读取地址1000的浮点数这个f就是救命稻草。内网协议文档常省略细节必须通过Wireshark抓包验证字节布局否则AI Agent输出的“温度-273℃”会让现场工程师当场报警。3.3 文件协议WebDAV的权限黑洞国产信创文档库普遍采用WebDAV协议但权限模型与HTTP完全不同。GET /file.pdf返回403不是因为Token无效而是WebDAV需要先PROPFIND获取资源属性再GET下载。LangChain的FileLoader直接失败。我们实现WebDAV Tool时必须模拟完整DAV流程from requests.auth import HTTPDigestAuth class WebDAVTool(BaseTool): name document_fetch description Fetch documents from internal WebDAV repository def _run(self, doc_id: str) - bytes: base_url http://10.1.30.50/webdav/ dav_url f{base_url}{doc_id} # Step 1: PROPFIND to check permissions and get ETag propfind_body ?xml version1.0 encodingutf-8? D:propfind xmlns:DDAV: D:prop D:getcontentlength/ D:getlastmodified/ /D:prop /D:propfind response requests.request( PROPFIND, dav_url, authHTTPDigestAuth(user, pass), datapropfind_body, headers{Content-Type: application/xml}, timeout60 ) if response.status_code ! 207: raise Exception(fPROPFIND failed: {response.status_code}) # Step 2: GET with If-None-Match for cache control etag extract_etag_from_xml(response.content) # 自定义解析函数 headers {If-None-Match: etag} if etag else {} download_resp requests.get( dav_url, authHTTPDigestAuth(user, pass), headersheaders, timeout300 # 大文件下载需长超时 ) if download_resp.status_code 200: return download_resp.content else: raise Exception(fDownload failed: {download_resp.status_code}) # 在Agent中使用 pdf_content tool._run(report_2024Q3.pdf)这里HTTPDigestAuth是关键——内网文档库禁用Basic Auth必须用Digest Auth进行质询-响应认证。忽略这一步所有请求都会返回401。4. 安全策略枷锁在零信任环境下构建可信执行链内网安全不是“防火墙一开万事大吉”而是层层设防操作系统SELinux策略、数据库行级权限、应用层OAuth2 Scope限制、甚至硬件级TPM密钥保护。AI Agent必须证明自己“值得被信任”而非简单拥有Token。4.1 SELinux上下文为什么Agent进程被拒绝访问GPU某次部署Qwen2-7B时Agent在CentOS 7上始终无法加载CUDA驱动nvidia-smi命令正常但Python进程报错OSError: libcudart.so.12: cannot open shared object file。排查发现SELinux阻止了/usr/lib64/libcudart.so.12的加载# 查看SELinux拒绝日志 ausearch -m avc -ts recent | grep nvidia # 输出avc: denied { read } for pid12345 commpython namelibcudart.so.12 devdm-0 ino123456 scontextsystem_u:system_r:unconfined_service_t:s0 tcontextsystem_u:object_r:lib_t:s0 tclassfile解决方案不是关闭SELinux违反等保而是为Agent进程分配正确上下文# 创建自定义策略模块 cat agent_gpu.te EOF module agent_gpu 1.0; require { type unconfined_service_t; type lib_t; class file { read execute }; } allow unconfined_service_t lib_t:file { read execute }; EOF # 编译并加载 checkmodule -M -m -o agent_gpu.mod agent_gpu.te semodule_package -o agent_gpu.pp -m agent_gpu.mod semodule -i agent_gpu.pp这个.te文件明确授权unconfined_service_t类型进程读取lib_t类型文件比setsebool -P allow_unconfined_execmem 1更精准避免扩大攻击面。4.2 数据库行级权限Agent如何“看不见”不该看的数据内网数据库常启用行级安全策略RLS例如Oracle VPD或PostgreSQL RLS。Agent连接字符串中userai_agent但该用户只能看到department_id IT的记录。LangChain的SQLDatabaseChain若直接执行SELECT * FROM employees会返回空结果Agent却误判为“无员工数据”。我们必须在SQL查询中显式注入安全谓词from langchain.sql_database import SQLDatabase from langchain.agents import create_sql_agent from langchain.agents.agent_toolkits import SQLDatabaseToolkit # 创建数据库连接时指定RLS上下文 db SQLDatabase( engine, # 关键设置RLS会话变量 view_supportTrue, include_tables[employees], # 注入RLS参数适配不同DB rls_context{ oracle: BEGIN DBMS_RLS.ADD_POLICY(...); END;, postgresql: SET app.current_department IT; } ) # Tool层重写确保所有查询带RLS条件 class RLSQueryTool(BaseTool): name sql_query description Execute SQL query with Row-Level Security context def _run(self, query: str) - str: # 自动追加RLS WHERE条件 if WHERE not in query.upper(): query WHERE department_id IT else: query AND department_id IT return db.run(query)这个AND department_id IT不是硬编码而是从Agent的会话上下文如用户登录时携带的部门ID动态注入确保数据隔离不被绕过。4.3 TPM密钥绑定让模型权重文件不可复制某军工客户要求AI模型权重文件.bin/.safetensors必须绑定到特定服务器TPM芯片。即使硬盘被窃文件在其他机器上也无法加载。我们采用OpenSSLTPM2-TSS方案生成TPM绑定密钥# 初始化TPM2 tpm2_clear tpm2_createprimary -c primary.ctx tpm2_create -g 0x000b -G 0x0001 -u key.pub -r key.priv -C primary.ctx # 导出密钥用于加密 tpm2_readpublic -c key.ctx -f pem -o key.pem加密模型文件from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding import os def encrypt_model_with_tpm(model_path: str, key_pem: str): # 从TPM PEM密钥派生AES密钥 with open(key_pem, rb) as f: key_data f.read() # 实际中调用tpm2tss库获取密钥句柄 aes_key derive_aes_key_from_tpm(key_data) # 自定义函数 # AES-GCM加密模型文件 iv os.urandom(12) cipher Cipher(algorithms.AES(aes_key), modes.GCM(iv)) encryptor cipher.encryptor() with open(model_path, rb) as f: plaintext f.read() padder padding.PKCS7(128).padder() padded_data padder.update(plaintext) padder.finalize() ciphertext encryptor.update(padded_data) encryptor.finalize() # 保存IV和认证标签 with open(model_path .enc, wb) as f: f.write(iv encryptor.tag ciphertext)Agent加载时解密def load_encrypted_model(model_enc_path: str, tpm_handle: int) - torch.nn.Module: with open(model_enc_path, rb) as f: data f.read() iv data[:12] tag data[12:28] ciphertext data[28:] # 调用TPM2解密需tpm2tss-python绑定 aes_key tpm2_decrypt_key(tpm_handle, iv tag) # 伪代码 cipher Cipher(algorithms.AES(aes_key), modes.GCM(iv, tag)) decryptor cipher.decryptor() padded_plaintext decryptor.update(ciphertext) decryptor.finalize() unpadder padding.PKCS7(128).unpadder() plaintext unpadder.update(padded_plaintext) unpadder.finalize() # 加载解密后的模型 return torch.load(io.BytesIO(plaintext))TPM绑定使模型文件失去独立存在价值彻底解决“模型泄露”风险。这是内网AI Agent区别于公网部署的终极安全屏障。5. 运维闭环枷锁让AI Agent在无人值守时持续可靠内网运维的最大痛点不是“部署不上”而是“上线后没人管”。没有Prometheus监控、没有ELK日志、没有CI/CD流水线Agent可能静默故障数周而不被发现。我们必须构建最小可行运维闭环。5.1 无监控平台的指标采集用文本文件替代Prometheus内网禁止部署外部监控组件但我们仍需知道Agent是否存活、推理延迟是否飙升、Tool调用失败率。方案是本地文件指标系统import time import json from pathlib import Path class LocalMetrics: def __init__(self, metrics_dir: str /var/log/ai_agent/metrics): self.metrics_dir Path(metrics_dir) self.metrics_dir.mkdir(parentsTrue, exist_okTrue) def record_latency(self, tool_name: str, latency_ms: float): # 按小时分片避免单文件过大 hour_file self.metrics_dir / flatency_{time.strftime(%Y%m%d_%H)}.jsonl record { timestamp: time.time(), tool: tool_name, latency_ms: latency_ms, status: success if latency_ms 5000 else slow } with open(hour_file, a) as f: f.write(json.dumps(record) \n) def record_error(self, tool_name: str, error_type: str, message: str): error_file self.metrics_dir / errors.jsonl record { timestamp: time.time(), tool: tool_name, error_type: error_type, message: message[:200], # 截断长消息 traceback: # 生产环境不记录traceback避免敏感信息 } with open(error_file, a) as f: f.write(json.dumps(record) \n) # 在Tool中集成 metrics LocalMetrics() class CRMTool(BaseTool): def _run(self, query: str) - str: start time.time() try: response requests.get(...) latency (time.time() - start) * 1000 metrics.record_latency(crm_query, latency) return response.json() except Exception as e: latency (time.time() - start) * 1000 metrics.record_latency(crm_query, latency) metrics.record_error(crm_query, type(e).__name__, str(e)) raise运维人员每天用grep error_type.*Timeout /var/log/ai_agent/metrics/errors.jsonl | wc -l统计故障数用awk {sum$3} END {print sum/NR} /var/log/ai_agent/metrics/latency_20240615_14.jsonl计算平均延迟。没有 fancy dashboard但足够驱动问题响应。5.2 日志审计满足等保2.0的最小日志字段等保2.0要求记录“谁、何时、做了什么、结果如何”。LangChain默认日志只记录LLM输入输出缺少关键审计字段。我们重写Loggerimport logging from datetime import datetime class AuditLogger: def __init__(self, log_file: str /var/log/ai_agent/audit.log): self.logger logging.getLogger(ai_agent_audit) self.logger.setLevel(logging.INFO) handler logging.FileHandler(log_file) formatter logging.Formatter( %(asctime)s | %(levelname)s | %(session_id)s | %(user_id)s | %(tool_name)s | %(input)s | %(output_len)d | %(status)s | %(duration_ms)d ) handler.setFormatter(formatter) self.logger.addHandler(handler) def log_action(self, session_id: str, user_id: str, tool_name: str, input_data: str, output: str, status: str, duration_ms: float): # 敏感字段脱敏 safe_input self._mask_sensitive(input_data) safe_output_len len(output) if status success else 0 extra { session_id: session_id, user_id: user_id, tool_name: tool_name, input: safe_input, output_len: safe_output_len, status: status, duration_ms: int(duration_ms) } self.logger.info(, extraextra) def _mask_sensitive(self, text: str) - str: # 简单脱敏身份证、手机号、银行卡号 import re text re.sub(r\d{17}[\dXx], ***REDACTED_ID***, text) text re.sub(r1[3-9]\d{9}, ***REDACTED_PHONE***, text) text re.sub(r\d{4}\s?\d{4}\s?\d{4}\s?\d{4}, ***REDACTED_CARD***, text) return text[:200] # 限制长度 # 在Agent执行链中注入 audit_logger AuditLogger() def audit_wrapper(func): def wrapper(*args, **kwargs): start time.time() session_id kwargs.get(session_id, unknown) user_id kwargs.get(user_id, unknown) tool_name func.__name__ input_data str(args)[:500] if args else try: result func(*args, **kwargs) duration (time.time() - start) * 1000 audit_logger.log_action( session_id, user_id, tool_name, input_data, str(result), success, duration ) return result except Exception as e: duration (time.time() - start) * 1000 audit_logger.log_action( session_id, user_id, tool_name, input_data, , error, duration ) raise return wrapper这份日志可直接导入SIEM系统满足等保对“操作行为可追溯”的强制要求。5.3 静态配置热更新不用重启Agent就能改Tool参数内网变更窗口极短每次修改CRM地址都要重启Agent意味着服务中断。我们实现基于文件监听的配置热更新import threading import json from pathlib import Path class HotReloadConfig: def __init__(self, config_path: str): self.config_path Path(config_path) self.config self._load_config() self._start_watcher() def _load_config(self) - dict: try: with open(self.config_path, r) as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return {crm_url: http://10.1.20.15:8080} def _start_watcher(self): def watch_loop(): last_mtime 0 while True: try: mtime self.config_path.stat().st_mtime if mtime ! last_mtime: self.config self._load_config() last_mtime mtime print(f[INFO] Config reloaded at {datetime.now()}) except Exception as e: print(f[ERROR] Config watch failed: {e}) time.sleep(5) watcher_thread threading.Thread(targetwatch_loop, daemonTrue) watcher_thread.start() def get(self, key: str, defaultNone): return self.config.get(key, default) # 全局配置实例 config HotReloadConfig(/etc/ai_agent/config.json) # Tool中动态读取 class CRMTool(BaseTool): def _run(self, query: str) - str: crm_url config.get(crm_url, http://default) response requests.get(f{crm_url}/api/v1/customers, params{q: query}) return response.json()运维人员只需echo {crm_url: http://10.1.20.16:8080} /etc/ai_agent/config.jsonAgent在5秒内自动生效。这才是内网工程该有的敏捷性。6. 工程收束一份可立即执行的内网AI Agent检查清单以上所有技术点最终要沉淀为可执行的交付物。我给团队制定的《内网AI Agent上线前检查清单》如下每项都对应前述章节的实践检查项验证方法失败后果责任人网络连通性telnet 10.1.20.15 8080从Agent服务器直连目标系统Tool调用超时Agent返回“服务不可用”运维协议兼容性Wireshark抓包对比Agent请求 vs Postman成功请求数据解析错误LLM收到乱码开发SELinux上下文sesearch -s unconfined_service_t -t lib_t -c file -p readCUDA加载失败GPU推理不可用安全RLS策略注入执行SELECT * FROM employees确认返回结果含department_idIT过滤数据越权违反等保数据安全条款DBATPM密钥绑定将加密模型文件复制到另一台服务器尝试load_encrypted_model()模型加载失败报错KeyError: tpm_handle安全指标文件写入ls -l /var/log/ai_agent/metrics/latency_*.jsonl确认1小时内有新文件无法发现性能劣化故障定位延迟运维审计日志字段tail -n 1 /var/log/ai_agent/audit.log检查是否含session_iduser_idduration_ms等保测评不通过审计项缺失合规这份清单不是文档而是上线前的“手术核查表”。每次部署七个人运维、开发、安全、DBA、合规、测试、PM围坐在服务器前逐项打钩。当最后一项勾上才执行systemctl start ai-agent.service。最后分享一个真实体会在内网做AI Agent80%的时间花在“让系统承认你是合法居民”20%的时间才是“让AI聪明干活”。那些在公网教程里一笔带过的pip install、curl -X POST在内网中每个字符都可能是安全策略的雷区。真正的工程能力不在于调用多少个LLM API而在于你能否在没有Google、没有Stack Overflow、没有官方技术支持的真空里用tcpdump、Wireshark、strace和耐心一寸寸凿开协议壁垒。当你第一次看到Agent在完全离线的内网中准确读取PLC温度、调阅加密工单、生成带电子签章的报告时那种成就感远胜于任何云上Demo的掌声。