Azure Functions 部署 AutoGen 多智能体实战指南 1. 项目概述为什么要在 Azure Functions 上跑 AutoGen 多智能体我从去年开始在生产环境里落地 AI Agent 应用从最开始用 Flask 搭单体服务到后来上 Kubernetes 做弹性伸缩再到去年底彻底转向 Serverless 架构——不是为了赶时髦而是被真实业务场景逼出来的。我们团队做的是一套面向金融合规部门的实时舆情摘要系统每天要处理上千个监管关键词的突发新闻要求响应延迟必须控制在 3 秒内但流量又极不均衡早盘开盘前、重大政策发布后会出现短时峰值其余时间几乎为零。这时候你让运维半夜起来扩 Pod或者为每秒 0.2 个请求常年开着 4 核 8G 的 VM成本和体验都不可接受。AutoGen 0.4 正是我在这种背景下深度验证后选定的框架。它不像 LangChain 那样把所有逻辑塞进一个链式调用里也不像 LlamaIndex 那样强绑定 RAG 流程而是真正把“人”这个角色拆解成可编排、可替换、可审计的独立单元。比如我们最终上线的版本里一个典型任务流是用户输入“请分析最近一周关于‘碳关税’的欧盟政策动向”系统会自动启动 4 个 Agent——关键词扩展 Agent把“碳关税”扩展成“CBAM”“EU Carbon Border Adjustment Mechanism”等术语、多源检索 Agent并行调 Bing、Reuters API、欧盟官网 RSS、事实校验 Agent交叉比对不同信源的时间戳与措辞差异、合规摘要 Agent按银保监会《AI生成内容披露指引》第 3.2 条格式输出。这四个 Agent 各自专注一件事出问题只影响局部而不是整个链路崩掉。Azure Functions 成了这套架构的天然载体。它不是简单地把 AutoGen “搬上去”而是让 Serverless 的基因和 Agent 的协作范式产生了化学反应每个 HTTP 请求触发一次完整的多 Agent 协作会话函数执行完资源立即释放冷启动时间从早期的 8 秒压到现在的 1.2 秒后面会讲怎么做到的更重要的是它天然支持按毫秒计费——我们线上环境实测处理一个中等复杂度查询平均消耗 1420ms按 Azure 当前定价单次成本是 0.00037 元。算下来每月 50 万次调用云服务成本不到 200 元比租一台最低配 VM 还便宜。这不是理论值是我们财务系统导出的真实账单。你可能会问为什么不用 AWS Lambda 或 Google Cloud Functions我们做过三个月的横向对比测试。Azure Functions 对 Python 异步生态的支持更成熟特别是async for message in team.run_stream()这种流式响应模式在 Lambda 上需要额外封装成 WebSockets 才能实现而 Azure 原生支持 HTTP 流式传输另外 Azure OpenAI 的集成是开箱即用的不需要手动处理 token 刷新或 endpoint 路由这对快速迭代至关重要。当然如果你的基础设施已经在 AWS 生态里深耕多年那另当别论——技术选型永远要服务于你的现有资产而不是教条主义。这篇文章要讲的就是如何把这套经过生产验证的方案变成你能直接抄作业的完整流程。我会从最底层的环境约束讲起比如为什么必须用 Python 3.11 而不是 3.12因为 Azure Functions 当前 runtime 还没完全适配 3.12 的 async generator 行为到最关键的 Bing 搜索结果清洗技巧90% 的初学者在这里翻车返回的 HTML 里藏着大量广告脚本和动态加载的 div直接soup.get_text()会拿到一堆乱码再到部署时那个让所有人抓狂的pydantic版本冲突问题——这些都不是文档里写的而是我在凌晨三点盯着日志反复重试后记下的血泪经验。2. 整体架构设计与核心取舍逻辑2.1 为什么放弃传统微服务选择纯 Serverless Agent 编排很多人看到“多 Agent”第一反应是拆成多个微服务Search Service、Report Service、Validation Service……然后用 Kafka 或 RabbitMQ 做消息队列。我们在 PoC 阶段确实这么干过结果发现三个致命问题第一是状态同步成本爆炸。Agent 之间需要传递的不只是最终结果还有中间态比如搜索 Agent 返回了 5 个链接但校验 Agent 只需要其中 3 个的原始 HTML报告 Agent 又需要校验 Agent 输出的置信度分数。如果每个服务都存一份中间数据到 Redis光序列化/反序列化就吃掉 300ms如果用共享数据库事务锁又成了瓶颈。而 AutoGen 的GroupChat机制把所有消息存在内存队列里Agent 通过message.source和message.content直接读取上下文延迟压到 20ms 以内。第二是错误传播不可控。微服务里一个 Agent 报错上游服务得自己实现重试逻辑、降级策略、熔断开关。但在 AutoGen 的RoundRobinGroupChat里你可以精确控制max_turns3意味着每个 Agent 最多发言 3 次termination_conditionTextMentionTermination(TERMINATE)让报告 Agent 主动终结会话。我们在线上加了监控埋点发现 92% 的失败会话都在第 2 轮就因工具调用超时被自动终止根本不会污染后续流程。第三是调试成本高到无法忍受。微服务架构下你得同时看 4 个服务的日志再用 trace ID 关联。而 Azure Functions 的 Application Insights 会自动把整个 HTTP 请求生命周期里的所有 Agent 日志打上同一个 operation_id你在 KQL 里写一句requests | join (traces) on operation_Id | where message contains bing_Search_Agent就能拉出完整协作链路。这是生产力的代差。所以我们的架构图极其简单一个 HTTP 触发器 → 一个RoundRobinGroupChat实例 → 两个或更多AssistantAgent→ 各自调用封装好的FunctionTool。没有网关没有注册中心没有配置中心——所有状态都在单次函数执行周期内闭环。2.2 AutoGen 0.4 的关键升级为什么必须用这个版本AutoGen 在 0.3 到 0.4 的升级中重构了整个异步通信层。老版本用asyncio.Queue做消息中转新版本改用autogen_core提供的EventStream接口。这个改动看似微小实则决定了能否在 Serverless 环境稳定运行。我们对比过 0.3.12 和 0.4.0 的冷启动表现0.3 版本在 Azure Functions 上首次调用平均耗时 8.7 秒其中 6.2 秒花在初始化Queue和建立事件循环上0.4 版本优化了EventStream的懒加载机制首次调用压到 1.2 秒。这个差距不是数字游戏——金融客户要求首字响应时间 2 秒超过阈值就会触发 SLA 赔偿。更关键的是工具调用的可靠性。0.3 的FunctionTool在并发场景下会出现tool_call_id冲突导致 Agent 把 A 请求的搜索结果当成 B 请求的输入。0.4 引入了ToolCallContext每个工具调用都绑定唯一的request_id配合 Azure Functions 的context.invocation_id能精准追溯到每一次调用源头。我们在压力测试中模拟 100 并发请求0.3 版本报错率 17%0.4 版本是 0.3%。所以如果你还在用 0.3.x请立刻升级。升级过程要注意两点一是autogen_agentchat包名已改为autogen_core二是AzureOpenAIChatCompletionClient的参数签名变了——api_key改成api_key_provider需要传入一个返回字符串的 lambda 函数这是为了支持密钥轮换。我们线上用的是 Azure Key Vault 的get_secret方法封装代码片段后面会给出。2.3 Azure Functions 的 Runtime 选择Python 3.11 还是 3.10Azure Functions 官方支持 Python 3.10 和 3.11但我们的实测结论很明确必须用 3.11。原因有三首先是asyncio的性能提升。3.11 引入了TaskGroup和ExceptionGroup让RoundRobinGroupChat.run_stream()的异常处理更轻量。我们用相同负载测试3.10 下 100 并发的 P95 延迟是 2.8 秒3.11 是 2.1 秒。其次是依赖兼容性。AutoGen 0.4 依赖的httpx0.27 要求 Python 3.11而beautifulsoup44.12 的lxml后端在 3.10 上有内存泄漏问题——我们线上曾出现函数执行 50 次后内存占用飙升到 1.2GB重启才恢复。最重要的是冷启动优化。Azure Functions 的 Python 3.11 runtime 内置了importlib.metadata的缓存机制from autogen_core.tools import FunctionTool这种导入操作从 3.10 的 420ms 降到 3.11 的 180ms。别小看这 240ms它占到了冷启动总时间的 20%。当然3.11 也有坑它的typing模块对Literal类型的解析更严格如果你的工具函数签名里写了def bing_search(query: str, num_results: Literal[1,3,5])在 3.11 下会报TypeError: unsupported operand type(s)。解决方案是降级到typing_extensions或者直接用int类型加运行时校验——我们选后者因为更符合生产环境的防御性编程原则。2.4 工具链的精简哲学为什么只用 Bing 和 Azure OpenAI看到标题你可能觉得“就这两个工具太单薄了吧” 但我要告诉你这是经过 17 个业务场景验证后的最优解。我们最初设计了 7 个工具Bing、Google Scholar、SEC EDGAR、Reuters、彭博终端 API、维基百科、本地知识库。上线两周后砍掉了 5 个只剩 Bing 和 Azure OpenAI。砍掉的理由很现实工具越多失败率越高维护成本指数级上升。Google Scholar 的反爬机制让我们每周都要更新 UA 和 CookieSEC EDGAR 的 XML 解析规则半年一变彭博终端 API 的认证流程需要物理硬件密钥根本没法放进 Serverless 环境。最后保留 Bing是因为它有三个不可替代的优势一是微软官方 SDK 完善Ocp-Apim-Subscription-Key认证稳定二是搜索结果结构化程度高webPages.value字段始终包含name、url、snippet三个必填字段三是免费额度够用——我们每月 50 万次调用只用了 Bing Search API 免费层的 60%。Azure OpenAI 的选择更是深思熟虑。很多人用开源模型 vLLM 自建服务但我们算过账vLLM 集群的运维人力成本加上 GPU 服务器的折旧和电费单次推理成本是 Azure OpenAI 的 3.2 倍。更重要的是稳定性——我们线上用gpt-4oP99 延迟稳定在 800ms而自建的 Llama-3-70B 集群在流量高峰时 P99 延迟会飙到 4.2 秒且每天要处理 3-5 次 OOM Kill。Azure 的 SLA 是 99.95%我们合同里白纸黑字写着赔偿条款这才是企业级应用的底线。所以我的建议是先用最稳的两个工具跑通 MVP再根据真实业务反馈逐步扩展。我们下一个要接入的工具是 Azure AI Search因为它和 Bing 共享同一套认证体系SDK 也都是微软官方维护迁移成本几乎为零。3. 核心组件实现与避坑指南3.1 Bing 搜索工具的深度改造从“能用”到“好用”原教程里的bing_search函数有个严重缺陷它用requests.get(url)同步抓取网页这在 Azure Functions 的异步环境中会阻塞整个事件循环。我们线上遇到过最惨的一次某个广告页面加载了 12 个第三方 trackerget_page_content卡住 15 秒导致整个函数超时失败。解决方案是彻底重写为异步版本并加入三重防护import asyncio import aiohttp from bs4 import BeautifulSoup from typing import List, Dict, Optional async def bing_search_async( query: str, num_results: int 1, max_chars: int 500, timeout: float 8.0 # 总超时含搜索抓取 ) - List[Dict[str, str]]: 异步 Bing 搜索工具带超时控制和错误隔离 # 第一步Bing 搜索 API 调用同步但极快 api_key os.getenv(BING_SEARCH_KEY) if not api_key: raise ValueError(BING_SEARCH_KEY not found in environment variables) async with aiohttp.ClientSession() as session: try: # Bing 搜索本身超时设为 3 秒 async with session.get( https://api.bing.microsoft.com/v7.0/search, params{q: query, count: num_results}, headers{Ocp-Apim-Subscription-Key: api_key}, timeoutaiohttp.ClientTimeout(total3.0) ) as resp: if resp.status ! 200: raise Exception(fBing search failed: {resp.status}) data await resp.json() results data.get(webPages, {}).get(value, []) except asyncio.TimeoutError: raise Exception(Bing search timeout) except Exception as e: raise Exception(fBing search error: {str(e)}) # 第二步并发抓取网页内容异步带独立超时 tasks [] for item in results: # 每个网页抓取单独设置 5 秒超时避免单点拖垮全局 task asyncio.create_task( _fetch_single_page(item[url], max_chars, timeout5.0) ) tasks.append(task) # 使用 asyncio.gather 并发执行但捕获单个失败 page_contents await asyncio.gather(*tasks, return_exceptionsTrue) # 第三步结果组装过滤失败项 enriched_results [] for i, (item, content) in enumerate(zip(results, page_contents)): if isinstance(content, Exception): print(fFailed to fetch {item[url]}: {str(content)}) continue if content: # 确保有内容才加入 enriched_results.append({ title: item[name], link: item[url], snippet: item[snippet], body: content }) return enriched_results async def _fetch_single_page( url: str, max_chars: int, timeout: float 5.0 ) - Optional[str]: 单个网页抓取带 HTML 清洗 try: async with aiohttp.ClientSession() as session: async with session.get(url, timeoutaiohttp.ClientTimeout(totaltimeout)) as resp: if resp.status ! 200: return None html await resp.text() # 关键清洗移除 script/style 标签防止执行恶意 JS soup BeautifulSoup(html, html.parser) for tag in soup([script, style, nav, footer, header]): tag.decompose() # 提取纯文本但保留段落结构 text soup.get_text(separator\n, stripTrue) lines [line.strip() for line in text.split(\n) if line.strip()] # 按字符数截断但优先保留完整段落 content for line in lines: if len(content) len(line) 1 max_chars: break content line \n return content.strip() except asyncio.TimeoutError: return None except Exception as e: print(fError fetching {url}: {str(e)}) return None这个版本解决了五个关键问题超时分级搜索 API 3 秒单网页抓取 5 秒总流程 8 秒避免长尾请求拖垮整个函数错误隔离一个网页抓取失败不影响其他结果return_exceptionsTrue让gather继续执行HTML 安全清洗移除script/style标签防止 XSS 风险虽然 Agent 不直面用户但数据会进日志系统段落感知截断不是简单按字符切而是按\n分割后逐段累加确保返回的内容是语义完整的空结果过滤if content:避免把空字符串塞进 Agent 上下文导致模型胡说。提示我们线上还加了一层缓存用 Azure Cache for Redis 存query - results映射TTL 设为 300 秒。实测命中率 42%平均降低 350ms 延迟。缓存 key 用fbing:{hashlib.md5(query.encode()).hexdigest()[:8]}避免长 key 占用内存。3.2 Azure OpenAI 客户端的安全封装密钥管理与重试策略原教程里api_keyyour-azure-openai-api-key-here这种硬编码方式在生产环境是自杀行为。Azure Functions 提供了更安全的方案使用 Azure Key Vault Managed Identity。步骤如下在 Azure Portal 创建 Key Vault启用“软删除”和“清除保护”为你的 Function App 开启系统分配的 Managed Identity在 Key Vault 的“访问策略”里给该 Identity 授予Get权限把 OpenAI 密钥存为 Secret名称设为AZURE-OPENAI-KEY在 Function App 的“配置”里添加应用设置KEY_VAULT_URL值为 Key Vault 的 DNS 名称如https://myvault.vault.azure.net/。客户端代码这样写from azure.keyvault.secrets import SecretClient from azure.identity import ManagedIdentityCredential from autogen_ext.models.openai import AzureOpenAIChatCompletionClient # 初始化 Key Vault 客户端单例避免重复创建 _credential ManagedIdentityCredential() _vault_url os.getenv(KEY_VAULT_URL) _secret_client SecretClient(vault_url_vault_url, credential_credential) def get_openai_api_key() - str: 安全获取密钥带重试和缓存 try: # 先查内存缓存函数实例内有效 if not hasattr(get_openai_api_key, _cached_key): secret _secret_client.get_secret(AZURE-OPENAI-KEY) setattr(get_openai_api_key, _cached_key, secret.value) return getattr(get_openai_api_key, _cached_key) except Exception as e: # 重试 2 次每次间隔 1 秒 for i in range(2): try: secret _secret_client.get_secret(AZURE-OPENAI-KEY) setattr(get_openai_api_key, _cached_key, secret.value) return secret.value except: if i 1: raise e time.sleep(1) raise e # 创建客户端注意 api_key_provider 参数 az_model_client AzureOpenAIChatCompletionClient( azure_deploymentgpt-4o, modelgpt-4o, api_version2024-08-01-preview, azure_endpointhttps://your-service-name.openai.azure.com/, api_key_providerget_openai_api_key # 传入函数不是字符串 )这个封装带来了三重保障密钥不落地密钥只存在于 Key VaultFunction App 内存里只有临时副本自动轮换Key Vault 支持密钥自动轮换客户端无感故障隔离get_openai_api_key里的重试逻辑避免 Key Vault 瞬时抖动导致整个函数失败。注意AzureOpenAIChatCompletionClient的api_key_provider必须是 callable不能是字符串。这是 0.4 版本强制要求否则会报TypeError: expected callable。3.3 Agent 的角色定义与提示词工程让它们真的“各司其职”很多初学者以为 Agent 就是换个名字的 LLM 调用其实不然。Agent 的核心是角色契约——它必须清楚知道自己能做什么、不能做什么、什么时候该停止。我们为两个 Agent 设计的系统提示词经过 37 轮 A/B 测试才定稿# Bing Search Agent 的 system_message关键在最后一句 bing_search_agent AssistantAgent( namebing_Search_Agent, model_clientaz_model_client, tools[bing_search_tool], descriptionSearch Bing for information, returns top 1 result with a snippet and body content, system_message( You are a precise web search specialist. Your ONLY job is to call the bing_search tool with the exact user query. Do NOT modify the query, do NOT add explanations, do NOT generate any text. After calling the tool, immediately output ONLY the raw JSON result from the tool, without wrapping it in markdown or adding commentary. If the tool fails, output TOOL_CALL_FAILED and stop. ) ) # Report Agent 的 system_message关键在终止机制 report_agent AssistantAgent( nameReport_Agent, model_clientaz_model_client, descriptionGenerate output only based on search results, system_message( You are a senior research analyst at a financial compliance firm. Your job is to synthesize ONLY the information from the bing_Search_Agent into a concise, factual report. Follow these rules strictly: 1. Never invent facts not present in the search result. 2. If the search result is empty or contains TOOL_CALL_FAILED, output NO_RELEVANT_INFORMATION_FOUND. 3. Use formal business English, no markdown, no bullet points. 4. End your response with exactly TERMINATE on a new line, nothing else. ) )为什么这么写看几个真实案例Case 1用户问“比特币价格”Bing Agent 返回{title: CoinGecko, link: ..., snippet: Bitcoin price is $62,340, body: ...}Report Agent 输出As of today, Bitcoins price is $62,340 according to CoinGecko. TERMINATE✅ 完美。Case 2用户问“量子计算最新突破”Bing Agent 因网络问题返回TOOL_CALL_FAILEDReport Agent 输出NO_RELEVANT_INFORMATION_FOUND✅ 符合预期不会胡编。Case 3用户问“如何做蛋糕”无关领域Bing Agent 返回正常结果但 Report Agent 的提示词里有“financial compliance firm”它会主动忽略非金融内容输出NO_RELEVANT_INFORMATION_FOUND✅ 领域隔离生效。实操心得我们发现TERMINATE必须独占一行且前后不能有空格。早期版本写成Please terminate. TERMINATE导致TextMentionTermination(TERMINATE)无法匹配会话无限循环。这个细节在 AutoGen 文档里根本没提是我们在日志里逐行比对才发现的。3.4 RoundRobinGroupChat 的调优控制协作节奏与防死锁RoundRobinGroupChat看似简单但默认参数在 Serverless 环境下极易出问题。我们线上踩过的坑包括max_turns3太小金融类查询常需 4-5 轮交互比如搜索→校验→追问→再搜索→总结设成 3 会导致会话被强制截断enable_feedbackFalse的隐患默认关闭反馈但 Agent 有时需要知道上一轮是否成功我们开了反馈后 P95 延迟只增加 40ms却把失败率从 8% 降到 0.5%缺少超时控制run_stream()默认不设超时函数可能卡在某轮不动。最终采用的配置from autogen_agentchat.conditions import TextMentionTermination from autogen_agentchat.teams import RoundRobinGroupChat # 终止条件Report_Agent 输出 TERMINATE或总耗时超 15 秒 termination_condition TextMentionTermination(TERMINATE) team RoundRobinGroupChat( agents[bing_search_agent, report_agent], max_turns6, # 足够处理复杂查询 enable_feedbackTrue, # 开启反馈让 Agent 知道上一轮状态 termination_conditiontermination_condition, # 添加超时装饰器自定义 run_streamlambda *args, **kwargs: _timeout_wrapper( RoundRobinGroupChat.run_stream, *args, timeout15.0, **kwargs ) ) def _timeout_wrapper(func, *args, timeout15.0, **kwargs): 为 run_stream 添加超时包装 try: return asyncio.wait_for(func(*args, **kwargs), timeouttimeout) except asyncio.TimeoutError: raise Exception(fGroup chat timed out after {timeout} seconds)这个timeout_wrapper是关键。Azure Functions 的默认超时是 5 分钟但我们的业务 SLA 是 3 秒首字响应15 秒内必须结束。一旦超时函数主动抛异常Azure 会记录为失败触发告警我们运维能立刻介入。注意wait_for的timeout参数单位是秒不是毫秒。写成timeout15000会报TypeError: an integer is required这个错误信息极其误导实际是单位错了。4. 完整部署流程与生产级配置4.1 本地开发环境搭建从零开始的 7 步别跳过这一步。很多人的部署失败根源在本地环境就不一致。我们用的是 Windows WSL2 Ubuntu 22.04但步骤通用安装 Azure Functions Core Tools# Ubuntu curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor microsoft.gpg sudo install -o root -g root -m 644 microsoft.gpg /usr/share/keyrings/microsoft-archive-keyring.gpg sudo sh -c echo deb [archamd64 signed-by/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-debian-jammy-prod jammy main /etc/apt/sources.list.d/microsoft.list sudo apt-get update sudo apt-get install azure-functions-core-tools-4创建函数项目func init MyAutogenFunc --python --worker-runtime python --docker cd MyAutogenFunc func new --name autogen_func --template HTTP trigger --authlevel anonymous安装生产依赖注意版本锁定pip install autogen-core0.4.0,0.5.0 \ autogen-ext0.4.0,0.5.0 \ aiohttp3.9.0,4.0.0 \ beautifulsoup44.12.0,4.13.0 \ azure-identity1.14.0,2.0.0 \ azure-keyvault-secrets4.4.0,4.5.0 pip freeze requirements.txt创建本地密钥文件.envBING_SEARCH_KEYyour_bing_key_here KEY_VAULT_URLhttps://your-vault.vault.azure.net/ # 注意本地开发用环境变量生产用 Key Vault修改__init__.py核心逻辑把前面写的bing_search_async、get_openai_api_key、Agent 定义、team配置全部粘贴进去注意app.function_name装饰器要对应。本地测试关键验证点func start # 在另一个终端 curl http://localhost:7071/api/autogen_func?namelatestAIregulationinEU验证三件事是否返回Result: ...开头的字符串不是 500 错误日志里是否有bing_Search_Agent和Report_Agent的交替输出响应时间是否 3 秒用time curl ...测Dockerfile 适配为 Azure Container Apps 准备FROM mcr.microsoft.com/azure-functions/python:4-python311 ENV AzureWebJobsScriptRoot/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabledtrue COPY requirements.txt / RUN pip install -r /requirements.txt COPY . /home/site/wwwroot # 设置启动命令 CMD exec dotnet /azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost.dll提示func start默认用python3.10要指定--python-version 3.11否则本地和线上环境不一致。命令是func start --python-version 3.11。4.2 Azure Portal 部署12 个必填配置项详解登录 Azure Portal创建 Function App 时这 12 个配置项一个都不能错配置项推荐值为什么订阅你的付费订阅别选试用订阅额度不够资源组新建rg-autogen-prod隔离资源方便权限管理函数应用名称func-autogen-prod-region如func-autogen-prod-eastus全局唯一且含区域便于定位发布Code别选 Container我们用 zip deploy运行时堆栈Python明确指定版本3.11强制要求见前文区域与用户最近的区域我们选East USP95 延迟比West US低 120ms操作系统LinuxPython 3.11 runtime 只在 Linux 上可用计划类型Premium v3关键Consumption plan 不支持 WebSocket 流式响应且冷启动慢 3 倍高级选项 启用应用见解是必须开否则看不到 Agent 日志高级选项 启用托管标识是Key Vault 认证必需高级选项 允许匿名访问否安全起见后面用 API Management 加鉴权创建完成后进入 Function App 的“配置”页添加以下应用设置BING_SEARCH_KEY留空生产用 Key VaultKEY_VAULT_URLhttps://your-vault.vault.azure.net/WEBSITE_RUN_FROM_PACKAGE1启用 zip deployPYTHONPATH/home/site/wwwroot避免模块导入错误注意Premium v3计划的最小实例是 1 个但可以设置“始终开启”这样就没有冷启动。我们线上用的就是这个配置月成本约 $120换来的是 100% 的 2 秒首字响应。4.3 Zip Deploy 部署全流程从本地到生产的 5 分钟Azure CLI 部署是最可靠的方式。确保你已登录az login且有 Contributor 权限# 1. 打包在项目根目录 func azure functionapp publish func-autogen-prod-eastus --build-native-deps # 2. 如果失败手动打包推荐 cd MyAutogenFunc pip install --target ./dist --no-cache-dir -r requirements.txt cp -r *.py ./dist/ cd dist zip -r ../deploy.zip . cd .. func azure functionapp publish func-autogen-prod-eastus --no-build --dotnet-isolated # 3. 验证部署 curl https://func-autogen-prod-eastus.azurewebsites.net/api/autogen_func?nametest--no-build参数是关键。Azure Functions 的在线构建经常失败尤其是aiohttp编译 C 扩展时。我们用--no-build把所有依赖提前装好再 zip成功率 100%。部署后去 Portal 的“函数”页点击autogen_func在“代码测试”里能看到实时日志。发起一次测试请求你应该看到类似这样的输出2025-02-12T08: