1. 项目概述从“鞋匠的孩子没鞋穿”到打造个人AI副驾作为一名常年为他人设计和优化AI系统的架构师我发现自己陷入了一个经典的“鞋匠的孩子没鞋穿”的困境。每天我都在为团队构建智能助手、优化工作流、设计无服务器架构但自己的日常工作却依然充斥着重复、琐碎且耗神的“微摩擦”。比如每天要回答好几次“项目X的设计文档链接在哪”、“那个特定的gcloud命令语法是什么”、“能帮我review一下这段代码吗”。这些看似“快速”的任务累积起来却造成了巨大的认知负荷和效率损耗。我意识到我需要一个不一样的助手——不是一个通用的团队机器人而是一个真正了解我个人上下文、工作习惯和表达风格的个人AI副驾。这就是我构建mAIdAIMy AI Aid的初衷一个基于Google Cloud和Vertex AI深度集成在我日常工作流中的、高度个性化的智能助手。2. 核心设计思路构建你的“第二大脑”市面上的通用AI聊天机器人功能强大但它们缺乏对“我”这个个体的深度理解。它们不知道我负责哪些项目、我的代码风格偏好、我常用的内部工具链接更无法基于我的知识库进行推理。mAIdAI的设计核心就是构建一个围绕个人知识体系运作的“第二大脑”。2.1 交互模式的三元设计为了让这个“第二大脑”既智能又高效我设计了三种核心交互模式以平衡响应速度、成本和功能深度上下文感知对话这是助手的基础能力。所有对话都基于一个名为context.md的个人上下文文件。这个文件就像是助手的“系统指令”里面定义了我的角色、职责、常用项目信息、技术栈偏好、沟通风格等。每次与Gemini模型交互时这个上下文都会作为系统提示词注入确保回答是“为我量身定制”的。快速命令对于无需AI推理的静态信息查询我们追求零延迟、零成本。例如命令/links可以直接返回我常用的文档、仪表盘链接列表命令/snippet-aws-auth可以返回一段我常用的AWS身份验证代码模板。这类命令被预定义在配置中直接返回静态文本完全不调用大语言模型实现瞬间响应。斜杠命令这类命令用于需要AI介入但任务类型固定的场景。它本质上是一个“提示词模板”。例如当我输入/fix并附上一段报错的代码后端会自动将用户输入包装成“请调试并修复以下代码[用户代码]”的格式再发送给AI。这样我就不用每次都手动编写完整的调试请求极大地标准化和简化了工作流。类似的还有/rewrite重写文本、/summarize总结等。设计心得这种三元划分绝非随意。它直接对应了用户需求的频谱——从纯信息检索快速命令到结构化任务处理斜杠命令再到开放域复杂问题求解上下文对话。在架构设计初期就明确这种划分能有效指导后续的技术选型和资源分配避免将所有请求都“一刀切”地扔给昂贵的LLM从而在体验和成本间找到最佳平衡点。2.2 架构选型轻量、无服务器与事件驱动作为一个个人项目我的核心约束是“极简运维”和“低成本”。我不想在业余时间还要操心服务器监控、扩缩容或安全补丁。因此我选择了完全拥抱云原生的无服务器架构。前端/交互层直接复用Google Chat。这是最关键也最取巧的一步。我不需要开发任何自定义的UI界面。Google Chat本身就是我日常高频使用的协作工具将助手集成其中实现了“工具就在工作流里”的无缝体验。用户我自己只需要在Google Chat中像添加一个联系人一样添加这个机器人即可开始对话。传输层Google Chat通过HTTP Webhook将聊天事件消息实时推送到我指定的后端服务地址。这是一种标准、可靠的事件驱动通信模式。后端逻辑层一个部署在Google Cloud Run上的FastAPI应用。Cloud Run是一个完全托管的计算平台可以运行无状态容器。它最大的优点是“按需伸缩”和“缩容到零”——当没有请求时我不产生任何计算费用。FastAPI是一个现代、高性能的Python Web框架特别适合构建API其异步特性与Cloud Run的并发模型是天作之合。智能层后端服务通过Vertex AI API调用Gemini系列模型如gemini-1.5-flash。Vertex AI是Google Cloud统一的机器学习平台提供了稳定、高效的模型服务。我将个人的context.md内容作为系统指令system_instruction传递给Gemini使其回答具备高度的个性化。这个架构的整个数据流非常清晰用户在Google Chat中发送消息 - Google Chat通过Webhook调用Cloud Run上的FastAPI服务 - FastAPI解析消息类型快速/斜杠/对话- 根据需要组合提示词并调用Vertex AI - 将AI回复或静态命令结果返回给Google Chat - 用户收到回复。整个链条完全基于托管服务运维负担几乎为零。3. 核心实现细节与代码拆解项目的核心代码非常精简主要逻辑集中在一个main.py文件中。这得益于Google GenAI SDK和FastAPI的优秀设计。3.1 环境初始化与“第二大脑”加载一切始于正确的配置。我使用环境变量来管理敏感信息和可变配置这保证了代码的安全性和可移植性例如可以在不同项目间轻松部署。# main.py import os from pathlib import Path import google.generativeai as genai from google.generativeai import types # 初始化GenAI客户端连接到Vertex AI client genai.Client( projectos.environ[GOOGLE_CLOUD_PROJECT], # 从环境变量读取GCP项目ID locationos.environ[GOOGLE_CLOUD_LOCATION], # 从环境变量读取区域如 “us-central1” vertexaiTrue, # 关键标志指明使用Vertex AI端点而非通用API ) # 定义项目根目录并加载“第二大脑”——个人上下文文件 ROOT_FOLDER Path(__file__).parent MODEL_CONTEXT (ROOT_FOLDER / context.md).read_text(encodingutf-8) # 配置生成内容时的参数将个人上下文设置为系统指令 config types.GenerateContentConfig( system_instructionMODEL_CONTEXT, # 核心注入个性化知识 max_output_tokens5000, # 控制输出长度避免过长响应 # 可根据需要添加temperature等参数控制创造性 )context.md文件的内容就是助手的灵魂。它可能包含这样的内容你是mAIdAI我的个人技术助手。我的角色是AI解决方案架构师主要使用Python和GCP。 我当前负责的项目包括“Project Phoenix”一个数据流水线项目仓库链接...和“Project Atlas”一个微服务重构。 我的代码风格偏好使用f-string进行字符串格式化异常处理要具体函数要有类型注解。 我常用的内部工具链接监控仪表盘 - https://internal.example.com/dash 设计文档库 - https://docs.example.com。 当我问你关于“部署”时默认指的是使用Cloud Run和Terraform进行部署。 请用简洁、直接的技术口吻回答避免不必要的寒暄。实操要点system_instruction是引导模型行为最强大的工具之一。编写context.md时要像在给一个新入职的、非常聪明的实习生写工作指引一样。务必具体、结构化。可以分区块编写如“[个人简介]”、“[项目上下文]”、“[技术偏好]”、“[沟通风格]”。定期根据工作重点更新这个文件助手的能力也会随之进化。3.2 请求路由与交互类型处理FastAPI应用的核心是一个处理Webhook请求的路由。它需要解析来自Google Chat的复杂JSON事件格式并路由到正确的处理逻辑。# main.py from fastapi import FastAPI, Request app FastAPI() # 预定义的命令映射。在实际项目中这部分可以配置化例如从数据库或配置文件中读取。 QUICK_COMMANDS { “links”: “Here are my frequent links:\n- Design Doc Hub: ...\n- CI/CD Dashboard: ...“, “status”: “All systems operational. Last deployment: 2023-10-27.” } SLASH_COMMANDS { “fix”: “Please debug and fix the following code. Explain the issue and the solution:\n\nUSER INPUT: “, “rewrite”: “Rewrite the following text to be more concise and professional:\n\nUSER INPUT: “, “explain”: “Explain the following concept or code in simple terms:\n\nUSER INPUT: “ } app.post(“/“) async def index(request: Request) - dict: “”“处理Google Chat Webhook的主入口”“” event await request.json() # 从复杂的Google Chat事件结构中提取关键信息 message event.get(“message“, {}) argument_text message.get(“argumentText“, ““).strip() # 用户输入的完整文本 sender event.get(“sender“, {}).get(“displayName“, “User”) # 提取可能的“斜杠命令”部分例如 “/fix 这段代码错了” parts argument_text.split(maxsplit1) potential_command parts[0] if parts else “” user_input parts[1] if len(parts) 1 else “” # 路由逻辑 if potential_command in QUICK_COMMANDS: # 快速命令直接返回预定义的静态文本 response_text QUICK_COMMANDS[potential_command] elif potential_command in SLASH_COMMANDS: # 斜杠命令组合预设模板和用户输入然后调用AI prompt_template SLASH_COMMANDS[potential_command] full_prompt f“{prompt_template}{user_input}” response_text await generate_with_ai(full_prompt) else: # 默认上下文感知对话将用户整个输入作为对话内容 response_text await generate_with_ai(argument_text) # 按照Google Chat Card API的格式返回响应 return { “cardsV2”: [{ “cardId”: “responseCard”, “card”: { “header”: {“title”: f“mAIdAI for {sender}”}, “sections”: [{“widgets”: [{“textParagraph”: {“text”: response_text}}]}] } }] } async def generate_with_ai(prompt: str) - str: “”“调用Vertex AI Gemini模型生成内容”“” # 选择模型例如响应速度快、成本低的gemini-1.5-flash model client.get_model(“gemini-1.5-flash”) # 异步调用提高并发处理能力 response await model.generate_content_async(prompt, generation_configconfig) return response.text注意事项Google Chat Webhook的事件格式有其特定结构需要仔细处理argumentText、sender等字段。建议在开发时打印完整的event字典以便理解数据结构。另外返回的响应必须遵循Google Chat的cardsV2格式否则消息可能无法正确显示。对于快速命令确保返回的是纯文本或简单的Markdown因为Chat卡片对复杂格式的支持有限。3.3 异步处理与性能考量由于Cloud Run实例需要高效处理可能并发的Webhook请求虽然个人使用并发量不高但这是良好实践我全程使用了异步模式。# 使用 async def 定义异步路由处理函数 app.post(“/“) async def index(request: Request) - dict: ... # 使用GenAI客户端的异步方法调用模型 async def generate_with_ai(prompt: str) - str: model client.get_model(“gemini-1.5-flash”) # 注意是 generate_content_async response await model.generate_content_async(prompt, generation_configconfig) return response.text使用async/await和generate_content_async允许单个Cloud Run实例在等待AI模型响应这是一个I/O密集型操作时能够去处理其他传入的请求从而用更少的资源支撑更好的并发性能。这对于保持Cloud Run实例低成本和快速响应至关重要。4. 部署、配置与安全实践将代码变为可用的服务部署和配置是关键一步。我追求的是“一键式”或“极简式”部署。4.1 容器化与Cloud Run部署首先需要将应用容器化。一个简单的Dockerfile如下# 使用官方Python轻量级镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 声明容器监听的端口Cloud Run要求监听8080 ENV PORT8080 EXPOSE 8080 # 使用uvicorn启动FastAPI应用绑定到所有接口 CMD exec uvicorn main:app --host 0.0.0.0 --port ${PORT}然后使用gcloud命令行工具部署到Cloud Run# 在项目根目录构建并推送容器镜像假设已配置好Artifact Registry gcloud builds submit --tag gcr.io/YOUR-PROJECT-ID/maidai # 部署到Cloud Run设置必要的环境变量并允许未经验证的调用因为Google Chat需要调用它 gcloud run deploy maidai \ --image gcr.io/YOUR-PROJECT-ID/maidai \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --set-env-vars”GOOGLE_CLOUD_PROJECTYOUR-PROJECT-ID,GOOGLE_CLOUD_LOCATIONus-central1” \ --memory 512Mi --cpu 1部署技巧--allow-unauthenticated是为了让Google Chat的Webhook能访问你的服务。但这会公开你的端点。因此安全至关重要。一个更好的实践是在服务内部验证请求是否确实来自Google Chat。可以通过验证请求头中的令牌来实现Google提供了相关库如google-auth来验证Chat App的令牌。虽然原文未提及但在生产级个人应用中强烈建议加上。4.2 环境变量与模型配置所有可变配置都通过环境变量管理这包括GOOGLE_CLOUD_PROJECT你的GCP项目ID。GOOGLE_CLOUD_LOCATIONVertex AI服务区域。未来可以扩展GEMINI_MODEL如gemini-1.5-pro来控制使用的模型。在main.py中我们可以灵活地读取这些配置model_name os.environ.get(“GEMINI_MODEL”, “gemini-1.5-flash”) # 默认使用flash模型性价比高 model client.get_model(model_name)4.3 在Google Chat中配置机器人部署完成后你会获得一个Cloud Run服务的URL例如https://maidai-xyz.run.app。接下来需要在Google Chat API中配置你的机器人在Google Cloud Console中启用“Google Chat API”。创建或配置一个Chat应用。在“配置”部分将“连接设置”设置为“应用URL”并填入你的Cloud Run服务URL。发布应用可以选择“仅限你自己”作为可见性这样它就是你的私人机器人。配置完成后你可以在Google Chat中搜索并添加这个机器人就像添加一个联系人一样。至此你的个人AI助手就正式上线了。5. 进阶优化与扩展思路一个基础版本跑起来后我们可以从多个维度对它进行增强使其更强大、更智能。5.1 上下文记忆与对话历史目前的实现是无状态的每次对话都是独立的。为了实现多轮对话的记忆我们需要在后台维护一个会话上下文。一个简单的方法是为每个用户或每个聊天线程维护一个消息历史列表并在每次调用AI时将最近N轮的历史对话也作为上下文发送给模型。# 使用内存中的字典简单模拟生产环境应考虑Redis等持久化存储 conversation_history {} async def generate_with_ai(user_id: str, prompt: str) - str: model client.get_model(“gemini-1.5-flash”) # 获取或初始化该用户的历史 history conversation_history.get(user_id, []) # 将用户新输入加入历史 history.append({“role”: “user”, “parts”: [{“text”: prompt}]}) # 只保留最近K轮对话以控制令牌数 if len(history) 10: # 保留5轮对话user和assistant交替 history history[-10:] # 调用模型传入整个历史 response await model.generate_content_async(history, generation_configconfig) ai_response response.text # 将AI回复加入历史 history.append({“role”: “model”, “parts”: [{“text”: ai_response}]}) conversation_history[user_id] history return ai_response性能与成本提示维护对话历史会显著增加每次请求发送给模型的令牌数量从而增加延迟和成本。需要设置一个合理的上下文窗口大小例如最近10条消息。对于个人助手这通常是可接受的但需要监控Vertex AI的用量账单。5.2 工具调用与外部集成真正的“助手”应该能替你做事情而不仅仅是回答问题。通过Vertex AI的Function Calling函数调用能力可以让mAIdAI学会使用工具。例如你可以定义以下函数search_confluence(query): 在你的Confluence知识库中搜索文档。create_calendar_event(title, start_time, duration): 在你的Google日历中创建会议。query_project_tickets(status’open’): 从Jira获取你名下未解决的任务单。在代码中你需要用OpenAPI格式定义这些函数的模式。在调用Gemini时将这个函数列表传入。Gemini可能会在回复中指示需要调用某个函数并给出参数。你的后端代码执行该函数并将结果返回给Gemini由它整合成最终回答给用户。这能将mAIdAI从一个“知识库问答机”升级为一个可以主动操作外部系统的“智能代理”。5.3 成本监控与优化对于个人项目成本控制非常重要。主要成本来自Vertex AI的API调用。选择合适模型gemini-1.5-flash在响应速度和成本上取得了极佳的平衡非常适合对话场景。对于需要深度推理的复杂任务可以按需切换到gemini-1.5-pro。设置预算提醒在Google Cloud Console中为项目设置预算和警报当日费用达到一定阈值时发送邮件通知。优化上下文定期清理context.md移除过时信息保持精炼。冗长的上下文会增加每次调用的令牌数。利用缓存对于快速命令和常见的、答案变化不大的问题如“公司假期安排”可以在后端实现一个简单的缓存如TTL缓存在一定时间内直接返回缓存结果避免重复调用AI。6. 常见问题与故障排查在开发和运行mAIdAI的过程中你可能会遇到以下典型问题6.1 Google Chat Webhook 返回 404 或 500 错误问题在Google Chat中发送消息机器人无响应或在GCP日志中看到错误。排查检查URL确保在Google Chat API控制台中配置的“应用URL”完全正确并且是HTTPS。检查Cloud Run状态在Cloud Run控制台查看服务是否部署成功且处于“活跃”状态。查看日志流是否有应用启动错误如导入失败、环境变量缺失。检查端口确保Dockerfile和uvicorn命令中设置的端口默认8080与Cloud Run期望的一致。Cloud Run会将请求转发到容器内的PORT环境变量指定的端口。本地测试使用curl或 Postman 模拟Google Chat的Webhook请求体直接发送到本地运行的服务localhost:8080看是否能正确处理并返回正确的卡片格式。6.2 Vertex AI 调用失败或超时问题应用日志显示调用generate_content_async时出现权限错误或超时。排查服务账号权限Cloud Run默认使用一个计算引擎服务账号。确保这个服务账号拥有roles/aiplatform.user角色以便调用Vertex AI API。可以在IAM与管理 - IAM页面进行授权。API启用确认在项目中已启用“Vertex AI API”。模型可用区检查GOOGLE_CLOUD_LOCATION环境变量设置的区域如us-central1是否支持你所调用的Gemini模型。某些模型可能只在特定区域推出。异步超时Cloud Run请求有默认的超时时间最长可达60分钟但默认可能更短。如果AI模型响应非常慢可能导致请求超时。考虑在代码中为AI调用设置一个合理的超时时间并做好错误处理。6.3 上下文context.md似乎未生效问题AI的回答很通用不像“你”。排查文件路径与读取确认context.md文件在容器内的正确位置并且Python有权限读取。可以在启动日志中加入一行打印MODEL_CONTEXT的前100个字符来确认已成功加载。系统指令注入确认types.GenerateContentConfig中的system_instruction参数确实被设置为读取的文件内容。内容格式系统指令应是一段连贯的文本。避免使用过于复杂的Markdown格式如表格、复杂列表纯文本或简单段落效果最好。确保指令清晰、具体。6.4 快速命令/斜杠命令不触发问题输入/links没有返回静态链接而是触发了AI对话。排查命令解析逻辑检查路由函数中的命令解析逻辑。argumentText的提取是否正确命令映射字典QUICK_COMMANDS和SLASH_COMMANDS的键是否与用户输入匹配注意大小写和斜杠建议在解析后打印出potential_command和user_input的值进行调试。Google Chat的输入格式有时用户输入的消息前后可能带有空格。在解析前使用.strip()方法清理输入字符串。构建mAIdAI的过程与其说是一个开发项目不如说是一次对个人工作效率的深度审视和重构。它迫使你梳理出那些重复性的“知识痛点”和“操作摩擦”并用自动化的方式将其固化。这个系统的价值不在于技术的复杂性而在于它与“你”这个个体的契合度。当你的助手能准确说出你项目的简称、记得你上周刚研究过的技术难点、并以你习惯的风格回复消息时那种流畅感是任何通用工具都无法提供的。它不再是一个工具而是一个真正嵌入到你数字工作流中的认知延伸。
基于Google Cloud与Vertex AI构建个人AI助手:无服务器架构实践
1. 项目概述从“鞋匠的孩子没鞋穿”到打造个人AI副驾作为一名常年为他人设计和优化AI系统的架构师我发现自己陷入了一个经典的“鞋匠的孩子没鞋穿”的困境。每天我都在为团队构建智能助手、优化工作流、设计无服务器架构但自己的日常工作却依然充斥着重复、琐碎且耗神的“微摩擦”。比如每天要回答好几次“项目X的设计文档链接在哪”、“那个特定的gcloud命令语法是什么”、“能帮我review一下这段代码吗”。这些看似“快速”的任务累积起来却造成了巨大的认知负荷和效率损耗。我意识到我需要一个不一样的助手——不是一个通用的团队机器人而是一个真正了解我个人上下文、工作习惯和表达风格的个人AI副驾。这就是我构建mAIdAIMy AI Aid的初衷一个基于Google Cloud和Vertex AI深度集成在我日常工作流中的、高度个性化的智能助手。2. 核心设计思路构建你的“第二大脑”市面上的通用AI聊天机器人功能强大但它们缺乏对“我”这个个体的深度理解。它们不知道我负责哪些项目、我的代码风格偏好、我常用的内部工具链接更无法基于我的知识库进行推理。mAIdAI的设计核心就是构建一个围绕个人知识体系运作的“第二大脑”。2.1 交互模式的三元设计为了让这个“第二大脑”既智能又高效我设计了三种核心交互模式以平衡响应速度、成本和功能深度上下文感知对话这是助手的基础能力。所有对话都基于一个名为context.md的个人上下文文件。这个文件就像是助手的“系统指令”里面定义了我的角色、职责、常用项目信息、技术栈偏好、沟通风格等。每次与Gemini模型交互时这个上下文都会作为系统提示词注入确保回答是“为我量身定制”的。快速命令对于无需AI推理的静态信息查询我们追求零延迟、零成本。例如命令/links可以直接返回我常用的文档、仪表盘链接列表命令/snippet-aws-auth可以返回一段我常用的AWS身份验证代码模板。这类命令被预定义在配置中直接返回静态文本完全不调用大语言模型实现瞬间响应。斜杠命令这类命令用于需要AI介入但任务类型固定的场景。它本质上是一个“提示词模板”。例如当我输入/fix并附上一段报错的代码后端会自动将用户输入包装成“请调试并修复以下代码[用户代码]”的格式再发送给AI。这样我就不用每次都手动编写完整的调试请求极大地标准化和简化了工作流。类似的还有/rewrite重写文本、/summarize总结等。设计心得这种三元划分绝非随意。它直接对应了用户需求的频谱——从纯信息检索快速命令到结构化任务处理斜杠命令再到开放域复杂问题求解上下文对话。在架构设计初期就明确这种划分能有效指导后续的技术选型和资源分配避免将所有请求都“一刀切”地扔给昂贵的LLM从而在体验和成本间找到最佳平衡点。2.2 架构选型轻量、无服务器与事件驱动作为一个个人项目我的核心约束是“极简运维”和“低成本”。我不想在业余时间还要操心服务器监控、扩缩容或安全补丁。因此我选择了完全拥抱云原生的无服务器架构。前端/交互层直接复用Google Chat。这是最关键也最取巧的一步。我不需要开发任何自定义的UI界面。Google Chat本身就是我日常高频使用的协作工具将助手集成其中实现了“工具就在工作流里”的无缝体验。用户我自己只需要在Google Chat中像添加一个联系人一样添加这个机器人即可开始对话。传输层Google Chat通过HTTP Webhook将聊天事件消息实时推送到我指定的后端服务地址。这是一种标准、可靠的事件驱动通信模式。后端逻辑层一个部署在Google Cloud Run上的FastAPI应用。Cloud Run是一个完全托管的计算平台可以运行无状态容器。它最大的优点是“按需伸缩”和“缩容到零”——当没有请求时我不产生任何计算费用。FastAPI是一个现代、高性能的Python Web框架特别适合构建API其异步特性与Cloud Run的并发模型是天作之合。智能层后端服务通过Vertex AI API调用Gemini系列模型如gemini-1.5-flash。Vertex AI是Google Cloud统一的机器学习平台提供了稳定、高效的模型服务。我将个人的context.md内容作为系统指令system_instruction传递给Gemini使其回答具备高度的个性化。这个架构的整个数据流非常清晰用户在Google Chat中发送消息 - Google Chat通过Webhook调用Cloud Run上的FastAPI服务 - FastAPI解析消息类型快速/斜杠/对话- 根据需要组合提示词并调用Vertex AI - 将AI回复或静态命令结果返回给Google Chat - 用户收到回复。整个链条完全基于托管服务运维负担几乎为零。3. 核心实现细节与代码拆解项目的核心代码非常精简主要逻辑集中在一个main.py文件中。这得益于Google GenAI SDK和FastAPI的优秀设计。3.1 环境初始化与“第二大脑”加载一切始于正确的配置。我使用环境变量来管理敏感信息和可变配置这保证了代码的安全性和可移植性例如可以在不同项目间轻松部署。# main.py import os from pathlib import Path import google.generativeai as genai from google.generativeai import types # 初始化GenAI客户端连接到Vertex AI client genai.Client( projectos.environ[GOOGLE_CLOUD_PROJECT], # 从环境变量读取GCP项目ID locationos.environ[GOOGLE_CLOUD_LOCATION], # 从环境变量读取区域如 “us-central1” vertexaiTrue, # 关键标志指明使用Vertex AI端点而非通用API ) # 定义项目根目录并加载“第二大脑”——个人上下文文件 ROOT_FOLDER Path(__file__).parent MODEL_CONTEXT (ROOT_FOLDER / context.md).read_text(encodingutf-8) # 配置生成内容时的参数将个人上下文设置为系统指令 config types.GenerateContentConfig( system_instructionMODEL_CONTEXT, # 核心注入个性化知识 max_output_tokens5000, # 控制输出长度避免过长响应 # 可根据需要添加temperature等参数控制创造性 )context.md文件的内容就是助手的灵魂。它可能包含这样的内容你是mAIdAI我的个人技术助手。我的角色是AI解决方案架构师主要使用Python和GCP。 我当前负责的项目包括“Project Phoenix”一个数据流水线项目仓库链接...和“Project Atlas”一个微服务重构。 我的代码风格偏好使用f-string进行字符串格式化异常处理要具体函数要有类型注解。 我常用的内部工具链接监控仪表盘 - https://internal.example.com/dash 设计文档库 - https://docs.example.com。 当我问你关于“部署”时默认指的是使用Cloud Run和Terraform进行部署。 请用简洁、直接的技术口吻回答避免不必要的寒暄。实操要点system_instruction是引导模型行为最强大的工具之一。编写context.md时要像在给一个新入职的、非常聪明的实习生写工作指引一样。务必具体、结构化。可以分区块编写如“[个人简介]”、“[项目上下文]”、“[技术偏好]”、“[沟通风格]”。定期根据工作重点更新这个文件助手的能力也会随之进化。3.2 请求路由与交互类型处理FastAPI应用的核心是一个处理Webhook请求的路由。它需要解析来自Google Chat的复杂JSON事件格式并路由到正确的处理逻辑。# main.py from fastapi import FastAPI, Request app FastAPI() # 预定义的命令映射。在实际项目中这部分可以配置化例如从数据库或配置文件中读取。 QUICK_COMMANDS { “links”: “Here are my frequent links:\n- Design Doc Hub: ...\n- CI/CD Dashboard: ...“, “status”: “All systems operational. Last deployment: 2023-10-27.” } SLASH_COMMANDS { “fix”: “Please debug and fix the following code. Explain the issue and the solution:\n\nUSER INPUT: “, “rewrite”: “Rewrite the following text to be more concise and professional:\n\nUSER INPUT: “, “explain”: “Explain the following concept or code in simple terms:\n\nUSER INPUT: “ } app.post(“/“) async def index(request: Request) - dict: “”“处理Google Chat Webhook的主入口”“” event await request.json() # 从复杂的Google Chat事件结构中提取关键信息 message event.get(“message“, {}) argument_text message.get(“argumentText“, ““).strip() # 用户输入的完整文本 sender event.get(“sender“, {}).get(“displayName“, “User”) # 提取可能的“斜杠命令”部分例如 “/fix 这段代码错了” parts argument_text.split(maxsplit1) potential_command parts[0] if parts else “” user_input parts[1] if len(parts) 1 else “” # 路由逻辑 if potential_command in QUICK_COMMANDS: # 快速命令直接返回预定义的静态文本 response_text QUICK_COMMANDS[potential_command] elif potential_command in SLASH_COMMANDS: # 斜杠命令组合预设模板和用户输入然后调用AI prompt_template SLASH_COMMANDS[potential_command] full_prompt f“{prompt_template}{user_input}” response_text await generate_with_ai(full_prompt) else: # 默认上下文感知对话将用户整个输入作为对话内容 response_text await generate_with_ai(argument_text) # 按照Google Chat Card API的格式返回响应 return { “cardsV2”: [{ “cardId”: “responseCard”, “card”: { “header”: {“title”: f“mAIdAI for {sender}”}, “sections”: [{“widgets”: [{“textParagraph”: {“text”: response_text}}]}] } }] } async def generate_with_ai(prompt: str) - str: “”“调用Vertex AI Gemini模型生成内容”“” # 选择模型例如响应速度快、成本低的gemini-1.5-flash model client.get_model(“gemini-1.5-flash”) # 异步调用提高并发处理能力 response await model.generate_content_async(prompt, generation_configconfig) return response.text注意事项Google Chat Webhook的事件格式有其特定结构需要仔细处理argumentText、sender等字段。建议在开发时打印完整的event字典以便理解数据结构。另外返回的响应必须遵循Google Chat的cardsV2格式否则消息可能无法正确显示。对于快速命令确保返回的是纯文本或简单的Markdown因为Chat卡片对复杂格式的支持有限。3.3 异步处理与性能考量由于Cloud Run实例需要高效处理可能并发的Webhook请求虽然个人使用并发量不高但这是良好实践我全程使用了异步模式。# 使用 async def 定义异步路由处理函数 app.post(“/“) async def index(request: Request) - dict: ... # 使用GenAI客户端的异步方法调用模型 async def generate_with_ai(prompt: str) - str: model client.get_model(“gemini-1.5-flash”) # 注意是 generate_content_async response await model.generate_content_async(prompt, generation_configconfig) return response.text使用async/await和generate_content_async允许单个Cloud Run实例在等待AI模型响应这是一个I/O密集型操作时能够去处理其他传入的请求从而用更少的资源支撑更好的并发性能。这对于保持Cloud Run实例低成本和快速响应至关重要。4. 部署、配置与安全实践将代码变为可用的服务部署和配置是关键一步。我追求的是“一键式”或“极简式”部署。4.1 容器化与Cloud Run部署首先需要将应用容器化。一个简单的Dockerfile如下# 使用官方Python轻量级镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 声明容器监听的端口Cloud Run要求监听8080 ENV PORT8080 EXPOSE 8080 # 使用uvicorn启动FastAPI应用绑定到所有接口 CMD exec uvicorn main:app --host 0.0.0.0 --port ${PORT}然后使用gcloud命令行工具部署到Cloud Run# 在项目根目录构建并推送容器镜像假设已配置好Artifact Registry gcloud builds submit --tag gcr.io/YOUR-PROJECT-ID/maidai # 部署到Cloud Run设置必要的环境变量并允许未经验证的调用因为Google Chat需要调用它 gcloud run deploy maidai \ --image gcr.io/YOUR-PROJECT-ID/maidai \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --set-env-vars”GOOGLE_CLOUD_PROJECTYOUR-PROJECT-ID,GOOGLE_CLOUD_LOCATIONus-central1” \ --memory 512Mi --cpu 1部署技巧--allow-unauthenticated是为了让Google Chat的Webhook能访问你的服务。但这会公开你的端点。因此安全至关重要。一个更好的实践是在服务内部验证请求是否确实来自Google Chat。可以通过验证请求头中的令牌来实现Google提供了相关库如google-auth来验证Chat App的令牌。虽然原文未提及但在生产级个人应用中强烈建议加上。4.2 环境变量与模型配置所有可变配置都通过环境变量管理这包括GOOGLE_CLOUD_PROJECT你的GCP项目ID。GOOGLE_CLOUD_LOCATIONVertex AI服务区域。未来可以扩展GEMINI_MODEL如gemini-1.5-pro来控制使用的模型。在main.py中我们可以灵活地读取这些配置model_name os.environ.get(“GEMINI_MODEL”, “gemini-1.5-flash”) # 默认使用flash模型性价比高 model client.get_model(model_name)4.3 在Google Chat中配置机器人部署完成后你会获得一个Cloud Run服务的URL例如https://maidai-xyz.run.app。接下来需要在Google Chat API中配置你的机器人在Google Cloud Console中启用“Google Chat API”。创建或配置一个Chat应用。在“配置”部分将“连接设置”设置为“应用URL”并填入你的Cloud Run服务URL。发布应用可以选择“仅限你自己”作为可见性这样它就是你的私人机器人。配置完成后你可以在Google Chat中搜索并添加这个机器人就像添加一个联系人一样。至此你的个人AI助手就正式上线了。5. 进阶优化与扩展思路一个基础版本跑起来后我们可以从多个维度对它进行增强使其更强大、更智能。5.1 上下文记忆与对话历史目前的实现是无状态的每次对话都是独立的。为了实现多轮对话的记忆我们需要在后台维护一个会话上下文。一个简单的方法是为每个用户或每个聊天线程维护一个消息历史列表并在每次调用AI时将最近N轮的历史对话也作为上下文发送给模型。# 使用内存中的字典简单模拟生产环境应考虑Redis等持久化存储 conversation_history {} async def generate_with_ai(user_id: str, prompt: str) - str: model client.get_model(“gemini-1.5-flash”) # 获取或初始化该用户的历史 history conversation_history.get(user_id, []) # 将用户新输入加入历史 history.append({“role”: “user”, “parts”: [{“text”: prompt}]}) # 只保留最近K轮对话以控制令牌数 if len(history) 10: # 保留5轮对话user和assistant交替 history history[-10:] # 调用模型传入整个历史 response await model.generate_content_async(history, generation_configconfig) ai_response response.text # 将AI回复加入历史 history.append({“role”: “model”, “parts”: [{“text”: ai_response}]}) conversation_history[user_id] history return ai_response性能与成本提示维护对话历史会显著增加每次请求发送给模型的令牌数量从而增加延迟和成本。需要设置一个合理的上下文窗口大小例如最近10条消息。对于个人助手这通常是可接受的但需要监控Vertex AI的用量账单。5.2 工具调用与外部集成真正的“助手”应该能替你做事情而不仅仅是回答问题。通过Vertex AI的Function Calling函数调用能力可以让mAIdAI学会使用工具。例如你可以定义以下函数search_confluence(query): 在你的Confluence知识库中搜索文档。create_calendar_event(title, start_time, duration): 在你的Google日历中创建会议。query_project_tickets(status’open’): 从Jira获取你名下未解决的任务单。在代码中你需要用OpenAPI格式定义这些函数的模式。在调用Gemini时将这个函数列表传入。Gemini可能会在回复中指示需要调用某个函数并给出参数。你的后端代码执行该函数并将结果返回给Gemini由它整合成最终回答给用户。这能将mAIdAI从一个“知识库问答机”升级为一个可以主动操作外部系统的“智能代理”。5.3 成本监控与优化对于个人项目成本控制非常重要。主要成本来自Vertex AI的API调用。选择合适模型gemini-1.5-flash在响应速度和成本上取得了极佳的平衡非常适合对话场景。对于需要深度推理的复杂任务可以按需切换到gemini-1.5-pro。设置预算提醒在Google Cloud Console中为项目设置预算和警报当日费用达到一定阈值时发送邮件通知。优化上下文定期清理context.md移除过时信息保持精炼。冗长的上下文会增加每次调用的令牌数。利用缓存对于快速命令和常见的、答案变化不大的问题如“公司假期安排”可以在后端实现一个简单的缓存如TTL缓存在一定时间内直接返回缓存结果避免重复调用AI。6. 常见问题与故障排查在开发和运行mAIdAI的过程中你可能会遇到以下典型问题6.1 Google Chat Webhook 返回 404 或 500 错误问题在Google Chat中发送消息机器人无响应或在GCP日志中看到错误。排查检查URL确保在Google Chat API控制台中配置的“应用URL”完全正确并且是HTTPS。检查Cloud Run状态在Cloud Run控制台查看服务是否部署成功且处于“活跃”状态。查看日志流是否有应用启动错误如导入失败、环境变量缺失。检查端口确保Dockerfile和uvicorn命令中设置的端口默认8080与Cloud Run期望的一致。Cloud Run会将请求转发到容器内的PORT环境变量指定的端口。本地测试使用curl或 Postman 模拟Google Chat的Webhook请求体直接发送到本地运行的服务localhost:8080看是否能正确处理并返回正确的卡片格式。6.2 Vertex AI 调用失败或超时问题应用日志显示调用generate_content_async时出现权限错误或超时。排查服务账号权限Cloud Run默认使用一个计算引擎服务账号。确保这个服务账号拥有roles/aiplatform.user角色以便调用Vertex AI API。可以在IAM与管理 - IAM页面进行授权。API启用确认在项目中已启用“Vertex AI API”。模型可用区检查GOOGLE_CLOUD_LOCATION环境变量设置的区域如us-central1是否支持你所调用的Gemini模型。某些模型可能只在特定区域推出。异步超时Cloud Run请求有默认的超时时间最长可达60分钟但默认可能更短。如果AI模型响应非常慢可能导致请求超时。考虑在代码中为AI调用设置一个合理的超时时间并做好错误处理。6.3 上下文context.md似乎未生效问题AI的回答很通用不像“你”。排查文件路径与读取确认context.md文件在容器内的正确位置并且Python有权限读取。可以在启动日志中加入一行打印MODEL_CONTEXT的前100个字符来确认已成功加载。系统指令注入确认types.GenerateContentConfig中的system_instruction参数确实被设置为读取的文件内容。内容格式系统指令应是一段连贯的文本。避免使用过于复杂的Markdown格式如表格、复杂列表纯文本或简单段落效果最好。确保指令清晰、具体。6.4 快速命令/斜杠命令不触发问题输入/links没有返回静态链接而是触发了AI对话。排查命令解析逻辑检查路由函数中的命令解析逻辑。argumentText的提取是否正确命令映射字典QUICK_COMMANDS和SLASH_COMMANDS的键是否与用户输入匹配注意大小写和斜杠建议在解析后打印出potential_command和user_input的值进行调试。Google Chat的输入格式有时用户输入的消息前后可能带有空格。在解析前使用.strip()方法清理输入字符串。构建mAIdAI的过程与其说是一个开发项目不如说是一次对个人工作效率的深度审视和重构。它迫使你梳理出那些重复性的“知识痛点”和“操作摩擦”并用自动化的方式将其固化。这个系统的价值不在于技术的复杂性而在于它与“你”这个个体的契合度。当你的助手能准确说出你项目的简称、记得你上周刚研究过的技术难点、并以你习惯的风格回复消息时那种流畅感是任何通用工具都无法提供的。它不再是一个工具而是一个真正嵌入到你数字工作流中的认知延伸。