
1. 项目概述一个基于Gemini模型的技能开发框架最近在折腾AI应用开发特别是想快速把大语言模型的能力集成到自己的项目里发现了一个挺有意思的开源项目叫WJZ-P/gemini-skill。简单来说它就是一个专门为Google的Gemini系列大模型设计的技能开发框架。如果你用过LangChain或者Semantic Kernel这类工具可能会觉得它们功能强大但有时候也略显臃肿配置起来有点复杂。而这个gemini-skill框架在我看来它的定位非常清晰就是让你能更轻量、更直接地利用Gemini API来构建具备特定功能的“技能”。想象一下你有一个电商客服机器人需要它能理解用户关于订单的复杂查询比如“帮我查一下上周三晚上用支付宝付钱的那个订单物流到哪了”。传统的做法可能需要你写一堆规则或者用复杂的意图识别和实体抽取模型。但现在你可以基于Gemini模型用这个框架快速封装一个“订单查询技能”。这个技能内部会处理如何将用户自然语言转换成结构化的查询参数如何调用后端的订单API以及如何将API返回的原始数据组织成一段通顺的回复。gemini-skill就是帮你标准化这个“封装”过程的脚手架。它特别适合那些已经接入了Gemini API并且希望快速构建可复用、可组合的AI功能模块的开发者。无论是做聊天机器人、智能助手、自动化流程还是数据分析工具你都可以把其中需要AI“思考”和“决策”的部分抽离出来做成一个独立的“技能”。这样一来你的主程序逻辑会清晰很多而且这些技能可以像乐高积木一样在不同的项目里重复使用和组合。接下来我就结合自己的使用经验把这个框架的核心设计、怎么上手、以及实际开发中会遇到哪些坑给大家掰开揉碎了讲清楚。2. 框架核心设计思路与优势解析2.1 为什么选择“技能”作为抽象单元在深入代码之前我们先聊聊设计哲学。gemini-skill选择“技能”作为核心抽象这背后有很强的实用性考量。在AI应用开发中我们经常面临一个矛盾大模型的能力是通用的、非结构化的而我们的业务需求是具体的、结构化的。比如Gemini可以和你聊天文地理但它并不知道怎么连接你的数据库去查用户订单。“技能”这个概念就是为了在这两者之间架起一座桥梁。一个技能本质上是一个有明确输入、输出和边界的功能模块。它封装了三样东西意图理解告诉模型这个技能是干什么的它应该处理什么样的问题。执行逻辑当模型判定需要调用这个技能时具体要运行什么代码可能是调用一个API、查询数据库、执行一个计算。结果格式化将执行代码得到的结果整理成模型能够继续处理或直接返回给用户的格式。这样做的好处显而易见。首先它实现了关注点分离。你的业务逻辑比如调用订单系统的HTTP请求和与大模型的交互逻辑比如构造Prompt、解析响应被分开了。业务逻辑开发者可以专注于写稳健的API调用代码而AI应用开发者则专注于如何设计技能的描述和交互流程。其次它极大地提升了可复用性。一个写好的“天气查询技能”既可以用在聊天机器人里也可以用在语音助手中甚至可以嵌入到一个自动化脚本里。最后它让整个系统的可维护性变得更好。当某个后端接口发生变化时你只需要修改对应技能的执行逻辑而不用去动整个对话管理或Prompt工程的部分。2.2 与主流框架的差异化定位你可能会问LangChain的Tool或者Semantic Kernel的Plugin不也是干这个的吗没错它们的概念非常相似。但gemini-skill的差异化优势就在于它的“轻”和“专”。轻量级依赖少它没有LangChain那么庞大的工具链和集成生态。它的核心就是围绕Gemini API做最直接的封装这意味着它的代码库更小学习曲线更平缓对于只想用Gemini快速实现功能的开发者来说心智负担更小。你不需要为了用一个小功能而去理解一整个复杂的框架体系。对Gemini的原生优化因为专为Gemini设计它在Prompt构造、函数调用Function Calling的格式处理、多模态输入的支持上可能会更贴合Gemini API的最新特性和最佳实践。框架作者可能会针对Gemini的某些独特行为比如对系统指令的响应方式、多轮对话的上下文管理做专门的优化这是通用框架暂时无法精细覆盖的。更简单的配置和部署从它的设计来看它追求的是“开箱即用”的体验。定义技能可能只需要一个Python类加几个装饰器配置集中在少数几个地方。这对于开发原型、内部工具或者中小型项目来说速度优势非常明显。当然这并不意味着它比LangChain更好而是场景不同。如果你的项目重度依赖多种模型比如同时用OpenAI和本地模型、需要复杂的链式调用Chain、或者要集成大量现成的工具如搜索引擎、维基百科那么LangChain仍然是更强大的选择。但如果你认准了Gemini想要一个简洁、直接的技能开发方式gemini-skill就是一个非常精良的“特种工具”。2.3 核心架构预览虽然我们还没看代码但可以推测出它的核心架构至少包含以下几个部分技能基类BaseSkill所有自定义技能的蓝图定义了技能必须有的属性如名称、描述和方法如执行逻辑。技能注册与管理器SkillRegistry/Manager负责收集所有定义的技能并在需要时提供给模型。模型根据对话上下文从注册表中选择最合适的技能来调用。Gemini客户端封装对官方的google.generativeai库进行了一层包装集成了技能调用所需的逻辑比如自动将技能列表转换成Gemini API能识别的函数声明Function Declaration并处理模型返回的函数调用请求。对话/会话管理维护与模型交互的上下文历史确保在多轮对话中技能调用是连贯和准确的。这个架构的目标是让开发者只需关心2.1中提到的三件事用自然语言描述你的技能、用代码实现技能、定义好输入输出。剩下的“如何让模型知道这个技能”、“如何触发它”、“如何把结果塞回对话”框架都帮你处理了。3. 从零开始定义你的第一个技能理论说了这么多手痒了吗我们直接动手创建一个实实在在的技能。假设我们要做一个“单位换算技能”它能把用户说的“5英里等于多少公里”这类问题转化成具体的计算并回答。3.1 环境准备与安装首先确保你有一个Python环境建议3.9以上并且已经拥有了Google AI Studio的API密钥。如果没有去Google AI Studio网站免费申请一个。# 1. 创建项目目录并进入 mkdir my-gemini-skills cd my-gemini-skills # 2. 创建虚拟环境推荐 python -m venv venv # Windows激活: venv\Scripts\activate # Mac/Linux激活: source venv/bin/activate # 3. 安装gemini-skill框架和官方SDK # 假设它已发布到PyPI安装命令可能如下 pip install gemini-skill google-generativeai # 4. 设置你的API密钥 # 方式一设置环境变量最安全特别是准备部署时 # 在终端执行export GOOGLE_API_KEY你的API密钥 # 或在代码中设置 import os os.environ[GOOGLE_API_KEY] 你的API密钥注意永远不要将API密钥直接硬编码在提交到版本控制系统的代码中使用环境变量或安全的密钥管理服务。3.2 技能类的基本结构现在我们来创建第一个技能文件比如unit_conversion_skill.py。# unit_conversion_skill.py from gemini_skill import Skill, skill # 假设框架的入口模块名 from pydantic import BaseModel, Field # 用于定义结构化输入输出 # 1. 定义技能的输入参数模型 # 这相当于告诉Gemini“调用我这个技能时你需要给我提供以下信息” class UnitConversionInput(BaseModel): value: float Field(description需要转换的数值例如 5) from_unit: str Field(description原始单位例如 miles, pounds, celsius) to_unit: str Field(description目标单位例如 kilometers, kilograms, fahrenheit) # 2. 定义技能的输出响应模型 # 这相当于告诉框架和模型“技能执行完后我会返回这样一个结构” class UnitConversionOutput(BaseModel): original_value: float original_unit: str converted_value: float converted_unit: str formula_used: str Field(description使用的换算公式说明) # 3. 创建技能类 # 使用装饰器 skill 来标记这是一个技能并为其提供元数据 skill( nameunit_converter, description将一种单位的数值转换为另一种单位。支持长度、重量、温度等常见单位。, input_modelUnitConversionInput, output_modelUnitConversionOutput ) class UnitConversionSkill(Skill): # 继承自框架的基类 Skill 单位换算技能的具体实现类 # 一个内部的换算字典实际项目中可以放到数据库或配置文件中 _CONVERSION_MAP { (miles, kilometers): 1.60934, (pounds, kilograms): 0.453592, (celsius, fahrenheit): lambda c: c * 9/5 32, (fahrenheit, celsius): lambda f: (f - 32) * 5/9, } # 必须实现的方法框架会调用这个方法来执行技能 async def execute(self, input_data: UnitConversionInput) - UnitConversionOutput: 执行单位换算的核心逻辑。 key (input_data.from_unit.lower(), input_data.to_unit.lower()) conversion self._CONVERSION_MAP.get(key) if conversion is None: # 如果找不到换算关系抛出一个清晰的错误模型会将其传达给用户 raise ValueError(f暂不支持从 {input_data.from_unit} 到 {input_data.to_unit} 的换算。) # 执行换算 if callable(conversion): # 处理像温度这样的非线性换算 result_value conversion(input_data.value) formula f{input_data.to_unit} {conversion.__doc__ or 特定公式计算} else: # 处理线性换算乘一个系数 result_value input_data.value * conversion formula f{input_data.to_unit} {input_data.from_unit} * {conversion} # 返回结构化的输出 return UnitConversionOutput( original_valueinput_data.value, original_unitinput_data.from_unit, converted_valueround(result_value, 4), # 保留4位小数 converted_unitinput_data.to_unit, formula_usedformula )代码逐行解析输入输出模型UnitConversionInputUnitConversionOutput这是整个技能定义中最关键的一步。它利用Pydantic库定义了技能接口的“契约”。Field(description...)里的描述至关重要Gemini模型正是依靠这些描述来理解每个参数的含义。你的描述越清晰、越像自然语言模型就越能准确地从用户提问中提取出对应的值。例如用户说“把5英里换成公里”模型就能匹配出value5, from_unitmiles, to_unitkilometers。结构化输出让技能的执行结果不再是模糊的文本而是机器可读的数据。这方便了后续处理比如把结果填入表格、触发下一个动作等。技能装饰器skill这个装饰器将你的普通Python类“注册”为一个技能。它提供的元数据是模型选择技能的依据。name技能的内部标识符简短唯一。description用一句话告诉模型这个技能能做什么。例如模型在思考“用户想换算单位”时会扫描所有技能的描述寻找匹配度最高的。这个描述要精准概括技能的能力边界。input_model/output_model绑定刚才定义的结构告诉框架如何序列化和反序列化数据。技能执行方法execute这是技能的业务逻辑核心。它是一个async异步方法因为与模型API的交互通常是异步的。方法接收一个已经解析好的input_data对象类型就是你的InputModel你可以像使用普通Python对象一样直接访问它的属性input_data.value。方法内部就是你的业务代码查表、计算、调用API等等。最后它返回一个OutputModel的实例。框架会负责将这个结构化的结果以合适的方式整合到模型的回复中。3.3 集成技能并运行对话技能定义好了怎么用它来和Gemini聊天呢我们需要一个主程序来把一切串起来。# main.py import asyncio from gemini_skill import GeminiSkillManager from unit_conversion_skill import UnitConversionSkill # 导入我们刚写的技能 import google.generativeai as genai # 配置Gemini客户端 genai.configure(api_keyos.getenv(GOOGLE_API_KEY)) async def main(): # 1. 初始化技能管理器 manager GeminiSkillManager(model_namegemini-1.5-pro) # 指定使用的Gemini模型 # 2. 向管理器注册我们的技能 # 你可以在这里注册任意多个技能 manager.register_skill(UnitConversionSkill()) # 3. 创建一个对话会话 chat_session manager.create_session() # 4. 开始对话循环 print(单位换算助手已启动输入‘退出’或‘quit’结束对话。) while True: try: user_input input(\n你: ) if user_input.lower() in [退出, quit, exit]: print(助手: 再见) break # 将用户输入发送给管理器它会自动处理技能调用逻辑 response await chat_session.send_message(user_input) print(f助手: {response.text}) # 如果你想看更底层的细节比如是否调用了技能、调用了哪个可以检查响应对象 # if response.function_calls: # print(f[调试] 调用了技能: {response.function_calls}) except KeyboardInterrupt: break except Exception as e: print(f出错: {e}) if __name__ __main__: asyncio.run(main())运行这个main.py你就可以在终端里和你的AI助手对话了。试试输入“5英里等于多少公里”“把20摄氏度转换成华氏度。”“10磅换算成千克。”你会发现助手不仅能给出答案还能在背后自动调用我们写的UnitConversionSkill完成结构化的计算。这就是框架的魅力——它把复杂的“意图识别 - 参数抽取 - 函数调用 - 结果整合”流程简化成了“定义技能 - 注册技能 - 聊天”三步。4. 高级技巧与实战经验分享掌握了基础技能创建后我们来看看如何打造更强大、更稳健的技能。这些技巧很多是文档里不会写的来自实际项目中的踩坑经验。4.1 设计高质量的技能描述与参数技能能否被准确调用80%取决于你的描述写得好不好。这本质上是一种“提示工程Prompt Engineering”。技能描述description要具体不要宽泛“进行单位换算”就不如“将长度、重量、温度等常见单位进行相互转换例如英里到公里、磅到千克、摄氏度到华氏度。”包含正面和反面例子可以在描述中暗示技能的边界。例如“本技能用于查询**当前**城市天气。如需查询历史天气或天气预报请使用其他技能。”使用动作性开头“计算...”、“查询...”、“翻译...”、“生成...”这有助于模型理解技能的行为。参数描述Field description描述“是什么”更要描述“为什么”和“格式”对于date参数不要只写“日期”可以写成“用户关心的日期格式为YYYY-MM-DD例如2023-10-27。如果用户说‘今天’则指当前日期。”枚举可能的值如果参数取值范围有限直接在描述里列出来。例如对于currency参数“货币的三字母代码例如USD美元、CNY人民币、EUR欧元。仅支持以下列表中的货币[USD, CNY, EUR, JPY, GBP]。”处理歧义有些参数容易混淆。比如一个“订餐技能”可能有delivery_time和pickup_time。描述要清晰区分“期望的外卖送达时间精确到分钟。”vs“用户自取餐点的预约时间。”4.2 实现复杂的技能逻辑与外部调用真实的技能很少只是查内存字典它们需要连接外部世界。异步HTTP请求大多数技能需要调用REST API。务必使用异步HTTP客户端如aiohttp或httpx避免阻塞整个对话线程。import httpx async def execute(self, input_data: WeatherInput) - WeatherOutput: async with httpx.AsyncClient() as client: # 注意这里的URL和API Key需要替换成真实的并妥善管理 params {city: input_data.city, key: os.getenv(WEATHER_API_KEY)} resp await client.get(https://api.weather.com/v3/..., paramsparams) resp.raise_for_status() data resp.json() # ... 解析data构造WeatherOutput ...数据库操作技能可能需要查询或更新数据库。建议在技能类初始化时注入一个数据库连接池或会话工厂而不是在每次execute中创建新连接。skill(...) class UserQuerySkill(Skill): def __init__(self, db_session_factory): self.db_session_factory db_session_factory async def execute(self, input_data): async with self.db_session_factory() as session: result await session.execute(query, params) # ... 处理结果 ...技能间的依赖与组合一个技能可以调用另一个技能。例如一个“旅行规划技能”可能会依次调用“天气查询技能”、“航班搜索技能”、“酒店比价技能”。你可以在技能管理器中通过技能名称来查找并调用其他技能。这要求你的技能设计是幂等的多次调用结果相同和无副作用的不依赖特定调用顺序以避免循环依赖和状态混乱。4.3 错误处理与用户友好反馈模型调用技能可能失败你的代码必须健壮。输入验证Pydantic模型提供了基础的类型验证但业务逻辑验证仍需在execute方法内进行。验证失败时抛出清晰的ValueError。async def execute(self, input_data: BookingInput): if input_data.guests 10: raise ValueError(抱歉目前无法在线预订超过10人的座位请致电餐厅。) if input_data.date date.today(): raise ValueError(预订日期不能是过去的时间。) # ... 正常逻辑 ...框架通常会捕获这些异常并将其内容作为模型回复的一部分返回给用户这样体验比直接抛出Python异常堆栈要好得多。外部服务降级当调用的API或数据库不可用时要有备用方案。例如天气API挂了可以返回缓存的历史数据或者坦诚地告诉用户“服务暂时不可用请稍后再试”而不是让整个对话卡死。async def execute(self, input_data): try: weather await self._fetch_live_weather(input_data.city) except httpx.RequestError: # 尝试从缓存获取 weather self._get_cached_weather(input_data.city) if weather: weather.note 温馨提示当前数据来自缓存可能非实时 else: raise ValueError(天气服务暂时无法访问请稍后再试。) return weather记录与监控在生产环境中务必记录技能每次被调用的详细信息输入参数、执行耗时、成功与否、错误信息。这能帮助你分析技能的使用频率、识别经常失败的场景从而持续优化。5. 调试、优化与生产部署考量开发完成后让技能在实际环境中稳定运行是另一个挑战。5.1 技能调用流程的调试技巧当技能没有被预期调用或者调用时参数不对你需要知道模型“脑子里”在想什么。启用详细日志查看框架或google-generativeai库的日志输出。通常可以设置日志级别为DEBUG来查看发送给模型的原始Prompt、模型返回的包含函数调用信息的响应。import logging logging.basicConfig(levellogging.DEBUG)检查“函数声明”在注册技能后你可以打印出框架为模型生成的函数声明Function Declaration。这能让你确认模型接收到的技能描述是否和你预期的一致。# 假设manager有相应方法 function_declarations manager.get_function_declarations() import json print(json.dumps(function_declarations, indent2, ensure_asciiFalse))模拟调用直接绕过模型用你预期的参数手动调用技能的execute方法测试业务逻辑是否正确。这是单元测试的一部分。5.2 性能优化与成本控制使用Gemini API是按Token计费的技能调用不当可能导致成本飙升或响应缓慢。精简上下文技能管理器会将对话历史和技能描述作为上下文发送给模型。如果对话轮次很多历史会很长。考虑设置一个合理的上下文窗口大小或者让框架只保留最近几轮对话。一些高级用法可以实现“摘要式”上下文将长篇历史压缩成一段摘要。优化技能描述冗长的技能描述会消耗大量Token。在保证清晰的前提下尽量精简description和每个参数的description。避免在描述中写入大量示例示例可以通过少量精炼的样本来体现。选择性提供技能不是每个用户问题都需要所有技能参与决策。可以根据对话的当前状态或用户身份动态地向管理器注册或注销技能。例如在客服场景中用户进入“支付”子流程时才提供“退款申请”、“支付状态查询”等技能。设置超时与重试对于调用外部API的技能必须设置请求超时。对于可重试的临时性错误如网络抖动可以实现简单的重试机制但要避免无限重试。import asyncio from tenacity import retry, stop_after_attempt, wait_exponential class ExternalAPISkill(Skill): retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) async def _call_external_api(self, params): async with httpx.AsyncClient(timeout10.0) as client: # 设置10秒超时 return await client.get(...)5.3 生产环境部署建议将基于gemini-skill的应用部署上线需要考虑以下几点API密钥管理使用环境变量或专业的密钥管理服务如AWS Secrets Manager, HashiCorp Vault来存储GOOGLE_API_KEY绝对不要写在代码或配置文件中。技能的热加载如果你需要在不重启服务的情况下新增或更新技能需要设计一套机制。例如技能管理器定期扫描某个目录下的Python文件或者监听一个配置中心的消息。这比简单的“注册所有技能”要复杂但对于需要高可用性的系统很重要。并发与异步确保你的整个服务栈是异步友好的。如果你的Web框架是同步的如Flask使用Gemini的异步API可能会遇到问题。考虑使用异步Web框架如FastAPI, Quart, Sanic来构建你的服务端点以便高效处理多个并发的用户请求和技能调用。版本化技能当技能的逻辑或输入输出模型发生变化时要考虑向后兼容性。可以为技能添加版本号或者通过不同的技能名称如unit_converter_v2来部署新版本同时逐步迁移用户流量。可观测性除了基本的日志集成监控和告警系统。监控技能的平均调用延迟、错误率、Token消耗量。当错误率超过阈值或响应时间异常时及时发出告警。6. 常见问题与排查实录在实际使用中你肯定会遇到各种问题。下面是我遇到的一些典型情况及其解决方法。6.1 模型不调用我的技能这是最常见的问题。用户的问题明明符合技能描述但模型就是选择用普通文本来回答而不是调用技能。可能原因1技能描述不够清晰或匹配度低。排查仔细阅读你的技能description。站在用户的角度用各种同义句去提问看描述是否能覆盖。比如你的技能是“查询当前天气”但描述是“获取天气信息”用户问“今天会下雨吗”模型可能觉得“获取天气信息”这个描述太宽泛不足以触发精确的函数调用。解决重写描述使其更具体、更具操作性。加入关键词。例如“根据提供的城市名查询该城市当前的实时天气状况包括温度、天气现象晴、雨、雪等、湿度和风速。”可能原因2输入参数模型定义有问题。排查检查input_model中每个字段的description。模型需要根据这些描述从用户话语中提取值。如果描述太模糊模型无法确定该提取什么。例如一个location字段描述是“地点”模型可能困惑于该提取“北京”还是“北京市朝阳区”。解决优化参数描述。例如“明确的城市名称例如北京、上海、纽约请不要包含‘市’、‘省’等后缀。对于国家请使用标准国家名例如美国、日本。”可能原因3模型“信心不足”。排查有时模型识别出了意图但觉得用户输入的信息不足以填充所有必填参数或者它对自己的判断不够自信于是选择用文本回应来索要更多信息。解决在系统指令System Instruction或对话历史中明确鼓励模型使用可用的工具。例如可以在初始化管理器时设置一个更强的系统提示“你是一个有帮助的助手并且可以使用工具。当用户的问题可以通过你拥有的工具解决时请务必调用相应的工具技能来获取准确信息。”6.2 技能被调用但参数解析错误模型决定调用技能了但传给execute方法的参数是错的比如类型不对、值荒谬。可能原因1参数描述存在歧义。案例一个“预订会议室”技能有duration参数描述为“会议时长”。用户说“订一个一小时的会”模型可能传duration1数值但你的代码可能期望的是duration”60″字符串分钟数或duration”01:00″时间字符串。解决在描述中明确规定格式和单位。“会议时长单位为分钟请输入纯数字例如60 表示一小时。”可能原因2模型进行了“创造性”解读。案例用户说“帮我订明天下午两点的位子”。date参数解析成了“明天”但你的代码期望YYYY-MM-DD格式。解决首先在参数描述中严格规定格式。其次在execute方法的开始加入一层数据清洗和标准化的逻辑。将“明天”、“下周”等相对日期转换为绝对日期将“下午两点”转换为“14:00”。from datetime import datetime, timedelta import re async def execute(self, input_data: BookingInput): # 清洗日期 cleaned_date self._normalize_date(input_data.date) # 清洗时间 cleaned_time self._normalize_time(input_data.time) # 使用清洗后的数据进行后续操作 ...6.3 技能执行慢或超时用户感觉响应迟钝。可能原因1外部API响应慢。排查在技能代码中记录调用外部服务的耗时。解决为HTTP请求设置合理的超时如5-10秒。考虑引入缓存对于短时间内相同的请求如多次查询同一城市天气直接返回缓存结果。对于非实时性要求极高的数据可以使用异步缓存更新策略。可能原因2模型生成速度慢。排查Gemini不同型号的模型速度不同。gemini-1.5-flash速度最快但能力稍弱gemini-1.5-pro能力更强但稍慢。解决根据场景选择模型。对于需要快速响应的简单问答可以使用flash型号。对于需要复杂推理和规划的任务再使用pro型号。也可以在代码中设置模型的generation_config限制最大输出Token数以加快生成速度。6.4 多技能冲突与选择当注册了多个技能且它们的描述有重叠时模型可能困惑于该调用哪一个。案例你有一个“查询天气”技能和一个“查询空气质量”技能。用户问“北京今天怎么样”这个问题两者都可能相关。解决细化技能边界让“查询天气”技能专注于温度、降水、风速等让“查询空气质量”技能专注于AQI、PM2.5等。在描述中明确区分。设计优先级或互斥如果逻辑上允许可以设定规则。例如在技能管理器中当模型返回多个可能的函数调用时可以根据技能名称或元数据设定一个简单的优先级规则。接受并处理有时这是无法完全避免的。可以设计一个“元技能”或让主控逻辑来处理如果模型调用了一个技能但用户不满意在下一轮对话中你可以通过系统指令引导模型尝试另一个相关技能。“用户似乎更关心环境质量请尝试使用‘查询空气质量’技能。”6.5 上下文管理混乱在多轮对话中技能调用可能丢失之前的上下文。现象用户先说“查一下北京的天气”助手调用技能成功回复。用户接着说“那上海呢”助手却可能直接调用技能并询问“你要查询哪个城市”而不是理解“上海”是上一轮“查询天气”意图的延续。解决这需要框架或你自己在会话层进行支持。高级的对话管理应该能维护“对话状态”。例如在技能执行后将当前的“意图”和关键参数如上一次的city北京暂存到会话状态中。当用户发出模糊指令时模型或你的逻辑可以尝试从会话状态中补全参数。gemini-skill框架如果本身不支持你可能需要在chat_session层面自己维护一个简单的状态机或者在发送给模型的系统指令中附带上一轮的关键信息。