🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度
这次我们来看一个能真正落地的企业级 AI Agent 自动化工作流方案。它不是停留在概念演示,而是一个基于 AWS Fargate、Claude Agent SDK 和 Model Context Protocol (MCP) 构建的,可以直接处理你 Outlook 邮件的智能助手。核心思路是让 AI 自己调用工具、执行任务,而开发者只需要定义好“做什么”(Skill)和“能做什么”(MCP 工具)。这篇文章将带你深度解析这套架构,并提供一个从零到一的端到端部署指南,让你理解如何让 AI Agent 自动化你的日常办公。
这套方案最值得关注的点在于其清晰的四层解耦架构:Agent 运行时、模型层、Skill 层和 MCP 工具层。这意味着你可以独立更换大模型、独立扩展业务场景、独立对接新的外部系统,而不会牵一发而动全身。对于企业环境,特别是需要处理中国区 Microsoft 365 服务的场景,它提供了完整的本地化配置和合规审计方案。本文将重点拆解如何利用这套架构,构建一个能自动整理未读邮件、起草回复草稿的 Outlook 助手,并探讨如何将其扩展到日历、文档、审批等更多场景。
1. 核心能力速览
在深入细节之前,我们先通过一个表格快速了解这个 AI Agent 自动化工作流方案的核心特性,判断它是否适合你的需求。
| 能力项 | 说明与特点 |
|---|---|
| 项目类型 | 企业级 AI Agent 自动化平台,基于 Claude Agent SDK 和 MCP 协议构建。 |
| 核心功能 | 自动化处理判断密集型办公任务,如邮件分类与回复起草、会议安排、文档归档、工单分诊等。通过 Skill 文件定义业务流程,通过 MCP 工具对接外部系统。 |
| 部署方式 | 容器化部署于 AWS Fargate(无服务器容器),支持弹性伸缩,无需管理底层服务器。 |
| 模型接入 | 模型层完全解耦。支持通过 API 接入第三方大模型(如 DeepSeek, Qwen),计费走 AWS Marketplace,方便中国区合规使用。也可替换为其他兼容 API 或私有化模型。 |
| 开发门槛 | 较低。业务逻辑通过 Markdown 格式的 Skill 文件描述,非技术人员可维护;系统集成通过开发标准化的 MCP Server 实现,代码复用性高。 |
| 关键特性 | 1.Skill 驱动:业务流程与代码分离,变更无需重新部署。 2.工具标准化:通过 MCP 协议统一外部系统接口。 3.安全可控:外发动作(如发邮件)默认设置为草稿/待审状态,需人工确认。 4.全链路审计:集成 AWS CloudWatch, ALB 日志,满足企业合规要求。 |
| 适合场景 | 企业内部的邮件处理、日程管理、文档流转、IT 服务台等重复性高且需要一定语义判断的办公自动化场景。 |
| 不适合场景 | 需要极低延迟(毫秒级)响应的实时交互场景;完全基于固定规则的简单流程(用传统 RPA 可能更经济)。 |
2. 架构深度解析:四层解耦设计
为什么说这个方案实现了“AI 构建 AI 工作流”?关键在于其高度模块化的四层架构。每一层职责清晰,变更成本低,这才是实现自动化工作流可持续演进的基础。
2.1 Agent 运行时层
这是工作流的大脑和调度中心。基于 Claude Agent SDK 构建,部署在 AWS Fargate 上。它本身不包含模型,主要职责是:
- 加载并解析 Skill:将 Markdown 格式的 Skill 文件作为系统提示词(System Prompt)加载,指导 Agent 的行为逻辑。
- 管理推理循环:内置完整的“规划-执行-观察”循环。根据用户输入和 Skill 指引,决定调用哪个工具、传递什么参数。
- 集成 MCP 工具:以子进程方式启动 MCP Server,并通过标准输入输出(stdio)与之通信,动态发现和调用工具。
- 维持会话上下文:在多轮交互中保持对话历史,使 Agent 具备连贯执行多步骤任务的能力。
开发者无需手动编写复杂的 Agent 状态机和工具调用逻辑,SDK 已经封装了这些通用能力。
2.2 模型层
这是工作流的“智力”来源,被设计为完全可替换的组件。方案通过 HTTP API 调用外部大模型服务,例如示例中使用的硅基流动(SiliconFlow)提供的 Anthropic 兼容 API。
- 解耦价值:模型选型不再是一个架构级决策。你可以根据成本、性能、合规性(如数据不出境)需求,随时在 AWS Marketplace 上切换不同的模型供应商,只需修改
baseURL和API Key。 - 中国区合规:对于 AWS 中国区(北京/宁夏)用户,由于 Amazon Bedrock 暂不可用,通过 Marketplace 订阅第三方模型服务是合规且便捷的路径,所有费用统一体现在 AWS 账单中。
2.3 Skill 层
这是业务逻辑的载体,也是“低代码/无代码”理念的体现。每个具体的自动化场景(如“每日邮件简报”、“会议安排”)对应一个独立的.md文件。
- 内容:Skill 文件用自然语言和结构化标记描述任务的目标、触发条件、执行步骤、工具调用顺序、输出格式以及异常处理规则。
- 优势:
- 业务人员可维护:产品经理或业务专家可以直接修改 Skill 文件来优化流程,无需开发介入。
- 热更新:Skill 文件可以通过容器挂载卷(Volume)或配置中心动态加载,修改后立即生效,无需重启服务。
- 模型无关:同一份 Skill 可以在不同的大模型(如 Claude, GPT, DeepSeek)上运行,行为基本一致,降低了模型切换的迁移成本。
2.4 MCP 工具层
这是工作流与真实世界交互的手和脚。Model Context Protocol (MCP) 是一个开放协议,用于标准化 AI 应用与外部工具/数据源之间的通信。
- 工作原理:每个外部系统(如 Microsoft 365, Jira, 数据库)都对应一个 MCP Server。这个 Server 封装了该系统的所有 API,并以标准 JSON-RPC over stdio 接口暴露出一组“工具”(Tools)。
- 开发内容:开发者需要为每个要集成的系统编写一个 MCP Server,实现其
tools/list(列出工具)和tools/call(调用工具)的接口。例如,outlook-mcpServer 就封装了读取邮件、创建日历事件等 Graph API。 - 价值:Agent 运行时无需关心 Outlook 或 Jira 的具体 API 细节,它只统一地与 MCP 协议交互。新增一个系统,只需新增一个 MCP Server,原有的 Skill 和 Agent 逻辑完全不受影响。
3. 环境准备与前置条件
在开始动手部署之前,请确保你已满足以下所有条件。这些是项目能够成功运行的基石。
- Microsoft 365 租户:你需要一个由世纪互联运营的 Microsoft 365 中国区商业版或企业版租户,并且拥有全局管理员权限,用于在 Azure 门户(中国区)注册应用程序。
- AWS 中国区账户:需要一个 AWS 中国(北京)或(宁夏)区域的账户。因为涉及中国区 Microsoft Graph 端点访问,建议服务部署在相同区域以降低延迟。
- 模型服务订阅:在 AWS Marketplace(中国区)订阅一个第三方大模型 API 服务,例如硅基流动(SiliconFlow),并获取有效的
API Key和Endpoint URL。确保所选服务商支持 Anthropic 或 OpenAI 兼容的 API 格式。 - 本地开发环境:
- AWS CLI v2:配置好具有足够权限(如创建 ECR、ECS、Secrets Manager 资源)的访问密钥。
- Docker:用于构建和推送容器镜像。
- Node.js 18+:用于运行和测试 MCP Server 及 Agent 代码。
- 合规与安全评估(至关重要):在将企业数据(尤其是邮件、日历内容)发送给第三方模型 API 前,必须由法务和安全团队评估数据流路径、供应商的数据处理政策、数据留存期限和跨境传输风险。必要时,需要在 Skill 中设计数据脱敏逻辑。
4. 端到端部署实战:构建 Outlook 邮件助手
我们将以“每日邮件简报”场景为例,一步步完成从系统集成、服务部署到功能验证的全过程。
4.1 第一步:注册并配置 Microsoft Graph 应用
这是让 Agent 能够访问你邮箱数据的关键步骤,任何配置错误都会导致 401 认证失败。
- 登录 Azure 门户(中国区):访问
https://portal.azure.cn,使用全局管理员账号登录。 - 创建新应用注册:
- 导航到“Azure Active Directory” -> “应用注册” -> “新注册”。
- 名称填写
outlook-mcp-aws。 - “支持的账户类型”选择“仅此组织目录中的帐户(单租户)”。
- “重定向 URI”暂时填写
http://localhost:3000/auth/callback(用于本地测试),部署到 Fargate 后需要补充 ALB 的 HTTPS 回调地址。
- 配置 API 权限:
- 在应用注册详情页,进入“API 权限” -> “添加权限” -> “Microsoft Graph” -> “委托的权限”。
- 添加以下权限:
Mail.Read,Mail.ReadWrite,Mail.Send,Calendars.Read,Calendars.ReadWrite,Files.Read,Files.ReadWrite,User.Read。 - 重要:添加后,点击“为代表…授予管理员同意”按钮,完成授权。
- 创建客户端密码:
- 进入“证书和密码” -> “客户端密码” -> “新客户端密码”。
- 描述写
aws-fargate-mcp,过期时间建议 12 个月。 - 立即复制并妥善保存“值”,离开页面后将无法再次查看。
- 记录关键信息:保存好“应用程序(客户端) ID”和“目录(租户) ID”。
4.2 第二步:在 AWS Secrets Manager 中安全存储凭证
永远不要将密钥硬编码在代码或镜像中。使用 AWS Secrets Manager 是安全的最佳实践。
打开终端,使用 AWS CLI 执行以下命令(请替换尖括号内的内容为你的实际信息,并确保区域正确):
# 存储 Microsoft Graph 凭证 aws secretsmanager create-secret \ --name outlook-mcp/graph-credentials \ --secret-string '{ "clientId": "<your-client-id>", "tenantId": "<your-tenant-id>", "clientSecret": "<your-client-secret>" }' \ --region cn-north-1 # 存储第三方模型 API 密钥 aws secretsmanager create-secret \ --name agent-runtime/model-api-key \ --secret-string '{ "apiKey": "<your-model-api-key>", "baseUrl": "<your-model-endpoint>" }' \ --region cn-north-14.3 第三步:开发 MCP Server(以 Outlook 为例)
这是对接中国区 Microsoft 365 的核心代码。关键点在于所有端点都必须使用中国区特定域名。
1. 认证模块 (graph-auth.js): 此模块负责处理 OAuth 2.0 授权码流程和令牌刷新。特别注意中国区的权威主机(Authority)和资源范围(Scope)。
// graph-auth.js const axios = require('axios'); const fs = require('fs').promises; const path = require('path'); // 从环境变量读取凭证(最终从 Secrets Manager 注入) const tenantId = process.env.MS_TENANT_ID; const clientId = process.env.MS_CLIENT_ID; const clientSecret = process.env.MS_CLIENT_SECRET; // !!!中国区特定端点 !!! const CN_AUTHORITY = 'https://login.partner.microsoftonline.cn'; const CN_GRAPH_ENDPOINT = 'https://microsoftgraph.chinacloudapi.cn/v1.0'; const CN_GRAPH_SCOPE = 'https://microsoftgraph.chinacloudapi.cn/.default'; // 关键!Scope 必须正确 const TOKEN_FILE = path.join(__dirname, '.m365_token.json'); async function getAccessToken() { let tokens; try { const data = await fs.readFile(TOKEN_FILE, 'utf8'); tokens = JSON.parse(data); } catch (err) { throw new Error('未找到本地令牌文件,请先完成 OAuth 授权流程。'); } // 检查令牌是否过期(预留 5 分钟缓冲) if (Date.now() >= (tokens.expires_at - 300 * 1000)) { console.log('访问令牌即将过期,尝试刷新...'); tokens = await refreshToken(tokens.refresh_token); await saveTokens(tokens); } return tokens.access_token; } async function refreshToken(refreshToken) { const params = new URLSearchParams(); params.append('client_id', clientId); params.append('client_secret', clientSecret); params.append('refresh_token', refreshToken); params.append('grant_type', 'refresh_token'); // 刷新时 Scope 可缩小到实际所需范围 params.append('scope', 'https://microsoftgraph.chinacloudapi.cn/Mail.Read offline_access'); const response = await axios.post( `${CN_AUTHORITY}/${tenantId}/oauth2/v2.0/token`, params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ); return { access_token: response.data.access_token, refresh_token: response.data.refresh_token || refreshToken, // 新 refresh_token 可能为空 expires_at: Date.now() + response.data.expires_in * 1000 }; } async function saveTokens(tokens) { await fs.writeFile(TOKEN_FILE, JSON.stringify(tokens, null, 2)); } module.exports = { getAccessToken };2. Graph API 客户端模块 (graph-client.js): 封装对 Microsoft Graph 中国区端点的调用。
// graph-client.js const axios = require('axios'); const { getAccessToken } = require('./graph-auth'); const GRAPH_BASE = 'https://microsoftgraph.chinacloudapi.cn/v1.0'; async function callGraphAPI(method, endpoint, data = null) { const token = await getAccessToken(); const url = `${GRAPH_BASE}${endpoint}`; try { const config = { method, url, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, validateStatus: () => true // 确保所有状态码都不会抛出异常,便于统一错误处理 }; if (data) { config.data = data; } const response = await axios(config); if (response.status >= 200 && response.status < 300) { return response.data; } else { // 提供详细的错误信息 const errorDetail = response.data?.error?.message || JSON.stringify(response.data); throw new Error(`Graph API 调用失败: ${method} ${endpoint} - HTTP ${response.status}: ${errorDetail}`); } } catch (error) { console.error(`[Graph Client] 请求出错: ${error.message}`); throw error; } } // 封装的工具函数 async function listUnreadEmails(userPrincipalName, top = 50) { const filter = `isRead eq false`; const select = `id,subject,from,receivedDateTime,bodyPreview,hasAttachments`; const endpoint = `/users/${userPrincipalName}/messages?$filter=${encodeURIComponent(filter)}&$select=${select}&$top=${top}&$orderby=receivedDateTime desc`; return await callGraphAPI('GET', endpoint); } async function createDraftReply(userPrincipalName, messageId, replyContent) { // 1. 创建回复草稿 const draft = await callGraphAPI('POST', `/users/${userPrincipalName}/messages/${messageId}/createReply`); // 2. 更新草稿内容 const updateEndpoint = `/users/${userPrincipalName}/messages/${draft.id}`; const updateData = { body: { contentType: 'HTML', content: replyContent } }; // 注意:这里只更新草稿,不调用 send API return await callGraphAPI('PATCH', updateEndpoint, updateData); } module.exports = { listUnreadEmails, createDraftReply };3. MCP Server 主文件 (server.js): 使用@modelcontextprotocol/sdk创建标准 MCP 服务器,将 Graph API 功能暴露为 Agent 可调用的工具。
// server.js const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { listUnreadEmails, createDraftReply } = require('./graph-client.js'); // 定义 MCP 工具 const tools = [ { name: "list_unread_emails", description: "列出指定用户的未读邮件,可按数量排序和筛选。", inputSchema: { type: "object", properties: { user: { type: "string", description: "用户主体名称,通常是邮箱地址。" }, top: { type: "number", description: "返回的邮件数量,默认50。", default: 50 } }, required: ["user"] }, handler: async ({ user, top }) => { try { const emails = await listUnreadEmails(user, top); return { content: [{ type: "text", text: JSON.stringify(emails, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `工具调用失败: ${error.message}` }], isError: true }; } } }, { name: "draft_reply", description: "为指定邮件创建一封回复草稿。草稿将保存在用户的草稿箱,不会自动发送。", inputSchema: { type: "object", properties: { user: { type: "string", description: "用户主体名称。" }, messageId: { type: "string", description: "要回复的邮件ID。" }, replyBody: { type: "string", description: "回复的HTML正文内容。" } }, required: ["user", "messageId", "replyBody"] }, handler: async ({ user, messageId, replyBody }) => { try { const result = await createDraftReply(user, messageId, replyBody); return { content: [{ type: "text", text: `回复草稿创建成功,草稿ID: ${result.id}` }] }; } catch (error) { return { content: [{ type: "text", text: `创建回复草稿失败: ${error.message}` }], isError: true }; } } } ]; // 创建 MCP 服务器 const server = new Server( { name: "outlook-mcp", version: "1.0.0" }, { capabilities: { tools: {} } } ); // 设置请求处理器 server.setRequestHandler("tools/list", async () => ({ tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })) })); server.setRequestHandler("tools/call", async (request) => { const { name, arguments: args } = request.params; const tool = tools.find(t => t.name === name); if (!tool) { return { content: [{ type: "text", text: `未找到工具: ${name}` }], isError: true }; } return await tool.handler(args); }); // 启动服务器,通过 stdio 与 Agent 运行时通信 async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Outlook MCP Server 已启动并通过 stdio 通信。"); } main().catch(console.error);4.4 第四步:容器化与部署到 AWS Fargate
1. 编写 Dockerfile:创建一个Dockerfile来构建 MCP Server 的镜像。
FROM public.ecr.aws/docker/library/node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY src ./src # 不直接暴露端口,因为 MCP 通过 stdio 通信 CMD ["node", "src/server.js"]2. 构建并推送镜像到 Amazon ECR:在项目根目录执行以下命令序列。
# 设置变量 ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) REGION=cn-north-1 REPO_NAME=outlook-mcp # 1. 创建 ECR 仓库(如果不存在) aws ecr create-repository --repository-name $REPO_NAME --region $REGION 2>/dev/null || echo "仓库可能已存在。" # 2. 登录到 ECR aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com.cn # 3. 构建镜像 docker build -t $REPO_NAME . # 4. 打标签并推送 docker tag $REPO_NAME:latest $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com.cn/$REPO_NAME:latest docker push $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com.cn/$REPO_NAME:latest3. 创建 ECS 任务定义和执行角色:这是 Fargate 运行容器的蓝图。关键是为任务配置正确的 IAM 角色,使其能从 Secrets Manager 读取密钥。
- 任务定义:在 Amazon ECS 控制台创建新的任务定义,选择 Fargate 启动类型。容器配置指向刚才推送的 ECR 镜像。环境变量(
MS_TENANT_ID,MS_CLIENT_ID,MS_CLIENT_SECRET)的值不要直接填写,而是通过“Secrets”选项,关联到之前在 Secrets Manager 创建的密钥。这样最安全。 - 任务执行角色:需要创建一个 IAM 角色(如
ecs-task-execution-role),并附加以下策略:- 托管策略
AmazonECSTaskExecutionRolePolicy(用于拉取镜像和写日志)。 - 自定义内联策略,允许从特定的 Secrets Manager 密钥读取(参考下方 JSON)。
- 托管策略
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret" ], "Resource": [ "arn:aws-cn:secretsmanager:cn-north-1:<你的账户ID>:secret:outlook-mcp/graph-credentials-*", "arn:aws-cn:secretsmanager:cn-north-1:<你的账户ID>:secret:agent-runtime/model-api-key-*" ] } ] }4. 创建 ECS 服务与负载均衡器:
- 集群与网络:在一个已有或新建的 ECS 集群中,创建 Fargate 服务。确保任务被部署在私有子网中,并且该子网有 NAT 网关或接口终端节点(VPC Endpoint)可以访问公网(用于调用模型 API 和 Microsoft Graph)。
- 负载均衡器:为服务创建一个 Application Load Balancer (ALB),监听 HTTPS 443 端口,并关联有效的 SSL 证书。ALB 部署在公有子网,将流量转发到私有子网中 Fargate 任务的动态端口。健康检查路径可以设置为
/healthz(需要在你的服务代码中实现)。 - 安全组:配置 Fargate 任务的安全组,仅允许来自 ALB 安全组的入站流量。出站规则需要允许访问 Microsoft Graph 端点(
*.chinacloudapi.cn)和你的模型服务商端点。
4.5 第五步:编写与测试 Skill
Skill 是业务逻辑的灵魂。以下是一个email-daily-brief.md的示例,它指导 Agent 如何完成每日邮件处理。
--- name: email-daily-brief description: 自动处理用户未读邮件,进行分类、摘要,并为需要回复的邮件起草回复草稿。 trigger: ["处理我的邮件", "检查未读邮件", "daily mail briefing"] --- # 每日邮件简报处理流程 ## 目标 自动登录用户的 Outlook 邮箱,获取未读邮件,进行分析和分类,生成摘要报告,并为需要回复的邮件创建回复草稿。 ## 执行步骤 1. **验证连接与获取邮件列表** - 调用 `list_unread_emails` 工具,传入当前用户的邮箱地址,获取最新的50封未读邮件。 - 如果返回认证错误(如401),告知用户“认证失败,请检查凭证或重新授权”。 2. **邮件分析与分类** - 遍历每一封邮件,调用 `read_email` 工具(需在 MCP 中实现)获取详细内容。 - 对每封邮件进行智能分类: - **需回复 (Need Reply)**: 邮件中包含直接问题、请求或需要确认的事项。 - **需行动 (Action Required)**: 邮件中包含待办事项、截止日期或需要你执行某个操作(如下载附件、填写表格)。 - **仅通知 (Notification)**: 新闻稿、系统通知、订阅内容等,仅需阅读。 - **垃圾/可忽略 (Spam/Ignore)**: 广告或无关紧要的邮件。 - 判断邮件的主要语言(中/英),后续回复需使用相同语言。 3. **起草回复(仅针对“需回复”类邮件)** - 对于标记为“需回复”的邮件,调用 `draft_reply` 工具。 - 回复内容模板: - **开头**: 礼貌问候(如“Hi [发件人名],”)。 - **正文**: 简要总结邮件核心问题,并给出明确、简洁的答复或下一步计划。 - **结尾**: 标准结尾(如“Best regards,”)和你的署名占位符 `[Your Name]`。 - **重要安全规则**:绝对不要调用任何“发送”邮件的工具。所有回复必须保存为草稿。 4. **生成汇总报告** - 输出一个结构化的 Markdown 格式报告,包含: - **概览**: 未读邮件总数,各类别数量统计。 - **邮件详情列表**: 每封邮件的发件人、主题、接收时间、分类结果和一句话摘要。 - **行动项**: 列出所有“需回复”和“需行动”的邮件,并附上已创建的草稿ID或行动建议。 - **提示**: 提醒用户登录 Outlook 客户端查看并发送草稿。 ## 输出示例 ```markdown # 每日邮件简报 - 2023-10-27 **概览** - 未读邮件总数: 23 - 需回复: 3 - 需行动: 2 - 仅通知: 15 - 可忽略: 3 **关键邮件详情** 1. **发件人**: tech-support@example.com **主题**: 关于服务器维护的询问 **分类**: 需回复 **摘要**: 询问本周六凌晨的维护窗口是否可行。 **状态**: 已创建回复草稿 (草稿ID: AAMkAG...)。 2. **发件人**: project-manager@example.com **主题**: Q3 项目报告初稿已上传 **分类**: 需行动 **摘要**: 请在下周三前审阅并反馈。 **行动**: 请前往 SharePoint 查看文档。 **下一步** - 请登录 Outlook,在“草稿箱”中查看并发送 3 封回复草稿。 - 处理 2 项待行动事项。这个 Skill 文件会被放置在容器内的 `/skills` 目录下,并在启动 Agent 时通过 `skillPath` 参数加载。 ### 4.6 第六步:启动 Agent 运行时并测试 最后,我们需要启动 Claude Agent SDK,它将加载 Skill 并连接 MCP Server。 ```javascript // agent-launcher.js const { Claude } = require('@anthropic-ai/claude-agent-sdk'); require('dotenv').config(); // 用于加载本地环境变量,生产环境从 Secrets Manager 注入 const agent = new Claude({ model: "deepseek-ai/DeepSeek-V3", // 模型名称,根据你的供应商调整 apiKey: process.env.MODEL_API_KEY, baseURL: process.env.MODEL_BASE_URL, // 例如 https://api.siliconflow.cn/v1 skillPath: "/app/skills/email-daily-brief.md", // Skill 文件在容器内的路径 mcpServers: { "outlook": { command: "node", args: ["/app/src/server.js"] // 指向 MCP Server 的入口文件 } } }); async function main() { console.log("开始执行每日邮件简报任务..."); // 这里的 userInput 可以触发 Skill 中定义的 trigger const response = await agent.send("请处理我的未读邮件,并生成简报。"); console.log("Agent 回复:", response.content); } main().catch(console.error);将上述代码、Skill 文件、MCP Server 代码一起打包成新的 Docker 镜像(或与 MCP Server 放在同一个镜像中),部署到另一个 Fargate 任务。这个任务同样需要配置从 Secrets Manager 读取模型 API Key 的权限。
5. 功能验证与效果评估
部署完成后,如何验证整个工作流是否跑通?
- 触发任务:通过一个简单的 HTTP 端点(可以在 Agent 服务中暴露一个
/trigger的 POST 接口)或定时任务(CloudWatch Events)来启动 Agent。发送触发指令,如“处理我的未读邮件”。 - 观察日志:在 Amazon CloudWatch Logs 中查看两个任务的日志:
- Agent 运行时日志:可以看到模型推理的过程、调用了哪些工具、得到了什么结果。
- MCP Server 日志:可以看到具体的 Graph API 调用是否成功,参数和返回值是什么。
- 检查结果:
- 直接输出:Agent 最终生成的 Markdown 格式简报会通过接口返回或写入日志/数据库。
- 间接效果:登录你的 Outlook Web 版或客户端,检查“草稿箱”中是否出现了由 Agent 创建的回复草稿。这是最直观的成功标志。
- 评估指标(PoC阶段):
- 成功率:任务完整执行(从触发到生成简报)的比例。
- 处理时长:从触发到完成的总时间。
- 草稿采纳率:人工检查后,认为 Agent 起草的回复草稿可直接发送或稍作修改即可发送的比例。
- 零误发:确保没有任何邮件被自动发送出去。这是安全底线。
6. 扩展:如何构建更多自动化场景?
邮件助手只是起点。基于四层架构,扩展新场景的边际成本很低。
- 日历智能调度:
- MCP 工具:在
outlook-mcp中增加find_meeting_times、create_calendar_event等工具。 - Skill:编写
schedule-meeting.md,描述如何解析邮件中的会议请求、查找双方空闲时间、创建会议邀约草稿(同样需人工确认发送)。
- MCP 工具:在
- 附件自动归档:
- MCP 工具:新增
files-mcp,封装 OneDrive/SharePoint API,提供save_to_onedrive、categorize_file等工具。 - Skill:编写
archive-attachments.md,描述如何识别邮件中的合同、发票附件,根据发件人和主题自动重命名,并保存到指定 SharePoint 库的相应文件夹。
- MCP 工具:新增
- 会议纪要提取待办:
- MCP 工具:新增
ticket-mcp,封装 Jira、ServiceNow 或内部工单系统的创建任务 API。 - Skill:编写
meeting-minutes-to-tasks.md,描述如何解析粘贴进来的会议纪要文本,识别出“行动项”(Action Items),并为每个行动项在项目管理工具中创建待办任务。
- MCP 工具:新增
扩展模式:新场景 = 现有/新的 MCP 工具 + 新的 Skill 文件。Agent 运行时和模型层无需改动。
7. 安全、合规与运维最佳实践
在企业中运行此类自动化 Agent,必须将安全合规放在首位。
- 最小权限原则:为每个 MCP Server 对应的 Azure AD 应用注册分配最小必需的 API 权限。邮件助手不需要访问用户的通讯录或 Teams 消息,就不要授予这些权限。
- 密钥生命周期管理:所有密钥(客户端密码、API Key)必须存储在 AWS Secrets Manager 中,并设置定期自动轮换策略。应用程序通过 IAM 角色动态获取。
- 数据脱敏与边界:在 Skill 中明确声明哪些字段可以发送给模型。对于高度敏感信息(如身份证号、银行账号),应在 MCP Server 层进行脱敏处理,用占位符替换后再传递给 Agent。
- 人工审批门:对于所有外发动作(发送邮件、发送会议邀请、创建工单、审批通过),设计上必须加入人工审批环节。例如,邮件只存草稿,会议邀请先存为待发送事件,工单创建后状态为“待确认”。
- 全链路审计:
- 启用 AWS CloudTrail 记录所有 API 调用。
- 将 Fargate 任务日志(包含模型请求/响应)和 ALB 访问日志统一发送到 Amazon S3,并启用对象锁定(Object Lock)以满足合规留存要求。
- 在 CloudWatch Logs 中设置指标过滤器,监控
ERROR、401、429(限流)等关键字,并触发 SNS 告警。
- 成本监控:监控模型 API 的调用次数和 Token 消耗,利用 AWS Cost Explorer 设置预算告警,防止因意外循环调用导致成本激增。
8. 常见问题与排查指南
在部署和运行过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查步骤 |
|---|---|---|
| MCP Server 启动失败,Agent 报连接错误 | 1. Node.js 依赖未安装或版本不对。 2. 环境变量(如数据库连接串)未正确注入。 3. 容器内执行路径错误。 | 1. 检查 Dockerfile 中的npm ci是否成功。2. 进入 Fargate 任务容器,执行 node src/server.js看独立运行是否报错。3. 确认 ECS 任务定义中 Secrets Manager 的密钥引用是否正确。 |
| 调用 Graph API 返回 401 InvalidAudience | Token 的受众(Audience/Scope)不正确,不是中国区端点。 | 这是最常见的问题。确保graph-auth.js中使用的CN_GRAPH_SCOPE是https://microsoftgraph.chinacloudapi.cn/.default,并且获取 Token 的请求也是发送到中国区权威主机login.partner.microsoftonline.cn。 |
| Agent 无法理解 Skill,或执行逻辑混乱 | 1. Skill 文件语法或路径错误。 2. 模型能力不足或对指令理解有偏差。 3. MCP 工具描述不够清晰。 | 1. 检查 Skill 文件格式,确保 Markdown 和 Front-matter 正确。 2. 尝试在 Skill 中提供更具体、更步骤化的指令。可以先用 ChatGPT/Claude 网页版模拟测试指令效果。 3. 优化 MCP 工具的描述( description)和输入参数模式(inputSchema),让模型更清楚工具的用途。 |
| 任务执行超时或中断 | 1. Fargate 任务配置的 CPU/内存不足。 2. 模型 API 响应慢或超时。 3. 处理邮件数量过多,步骤循环耗时太长。 | 1. 在 CloudWatch 中查看任务的内存利用率,适当调高任务定义中的内存限制(如从 1GB 升至 2GB)。 2. 在 Agent 配置中增加模型调用的超时时间。 3. 在 Skill 中限制单次处理邮件的数量(如 top=20),或设计分页处理逻辑。 |
| 无法从私有子网访问公网(模型API) | 私有子网没有配置 NAT 网关或网关路由不正确。 | 1. 确认 Fargate 任务所在的子网的路由表,是否有一条指向 NAT 网关(对于 IPv4)或 Egress-only 互联网网关(对于 IPv6)的默认路由(0.0.0.0/0)。2. 检查 NAT 网关是否已分配弹性 IP 并处于可用状态。 |
| CloudWatch 中看不到应用日志 | ECS 任务执行角色缺少logs:CreateLogStream和logs:PutLogEvents权限,或日志组不存在。 | 1. 确保任务执行角色附加的AmazonECSTaskExecutionRolePolicy策略包含对 CloudWatch Logs 的写入权限。2. 在 ECS 任务定义的“容器定义”中,确认“日志配置”已选择“awslogs”驱动,并正确指定了日志组和区域。 |
这个基于 AWS Fargate 和 Claude Agent SDK 的 AI Agent 自动化工作流方案,为我们展示了一条清晰的企业级 AI 应用落地路径。它成功地将前沿的 Agent 技术与现有的企业 IT 设施和合规要求相结合。其四层解耦的设计思想——尤其是 Skill 与 MCP 的分离——极大地提升了系统的可维护性和可扩展性。对于开发者而言,重心从编写复杂的 AI 逻辑,转移到了设计清晰的业务流程(Skill)和封装稳定的系统接口(MCP Server)上,这更符合软件工程的最佳实践。
建议你从“邮件简报”这个相对独立的场景开始实践,跑通从注册应用、部署服务到验证结果的完整闭环。在这个过程中,你会深刻体会到 MCP 协议如何简化工具集成,以及 Skill 文件如何降低业务逻辑的变更成本。一旦这个基础框架搭建完成,将其扩展到日历、文档、审批等场景将变得水到渠成。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度