Phi-3-Mini-128K智能体开发:基于Skills框架构建专属AI工作流
Phi-3-Mini-128K智能体开发基于Skills框架构建专属AI工作流最近在折腾一些轻量级模型的应用发现微软开源的Phi-3-Mini-128K虽然体积小但能力相当扎实。不过它和很多基础模型一样本身不具备调用外部工具的能力比如查个天气、算个数据或者搜索最新信息。这就让它的实用性打了折扣。正好社区里出现了不少围绕“Skills”或“Skill-Creator”这类框架的讨论。简单来说这些框架就像给模型装上了一套“瑞士军刀”让它能按需使用各种工具自己规划步骤去完成更复杂的任务。比如你直接对它说“帮我总结今天关于AI的新闻并生成一份报告”它就能自己决定先去搜索新闻然后筛选整理最后生成报告。今天我就结合自己的实践聊聊怎么用Skills框架的思路为Phi-3-Mini-128K注入“动手能力”打造一个真正能帮你干活的智能体。1. 为什么需要给模型添加“技能”你可能用过一些聊天机器人它们回答问题头头是道但一旦你让它做点实事比如“查一下北京明天飞上海的航班”它就无能为力了。这是因为大多数模型被训练成只进行“对话”它们的世界仅限于训练数据中的文本无法与真实世界互动。这就是“工具调用”或“技能扩展”要解决的问题。其核心思想是让模型学会在需要时主动选择并使用外部工具来获取信息或执行操作。想象一下你有一个非常得力的实习生他知识渊博模型本身的能力但只能动嘴不能动手。现在你给了他权限让他可以随时使用公司的搜索引擎、计算器、数据库查询系统各种技能。当他接到一个复杂任务时就能自己规划“先搜索资料再用计算器分析数据最后从数据库调取历史记录对比”然后一步步执行最终给你一个完整的结果。为Phi-3-Mini-128K这类轻量模型添加技能意义尤其大突破知识时效性限制模型训练数据有截止日期通过搜索技能它能获取最新信息。弥补计算与精确查询短板模型可能算错复杂数学或者记不清精确数据。调用计算器或数据库技能能保证结果的准确性。实现复杂工作流自动化将多个技能串联起来模型就能自主完成多步骤任务比如市场调研、数据分析报告生成等。2. 理解Skills框架的核心组件虽然具体的框架实现各有不同但一套完整的技能系统通常包含以下几个核心部分理解了它们你就能举一反三。2.1 技能Skill是什么技能就是一个具体的、可被模型调用的功能单元。每个技能都有明确的边界和用途。例如网络搜索技能输入关键词返回相关的网页摘要和链接。计算器技能输入数学表达式返回计算结果。数据库查询技能输入SQL语句或自然语言查询返回数据库中的记录。天气查询技能输入城市名返回天气信息。文件读写技能读取或保存特定格式的文件。你可以把技能想象成手机上的一个个App每个App负责一项专门的服务。2.2 技能描述与注册模型怎么知道它有哪些“App”可用呢这就需要“技能描述”。你需要用模型能理解的自然语言清晰地告诉它技能名称search_web技能功能在互联网上搜索相关信息。输入要求需要提供搜索关键词。输出说明会返回一段摘要和来源链接。这些描述会被注册到一个“技能库”中。当模型接到任务时它会先查看技能库判断是否需要以及需要调用哪个技能。2.3 任务规划与执行这是智能体的“大脑”部分。模型收到用户请求后比如“总结AI新闻并写报告”它会进行思考任务分解这个任务可以拆成几步可能需要“搜索新闻”、“总结内容”、“生成报告格式”。技能匹配每一步对应哪个技能第一步匹配search_web第二步和第三步可能用模型自身的文本能力。规划序列决定执行的先后顺序。执行与集成按顺序调用技能将上一步的输出作为下一步的输入最后整合所有结果生成给用户的回复。这个过程可以是模型自主完成的需要较强的推理能力也可以由框架引导完成。3. 动手实践为Phi-3-Mini构建技能工作流下面我将用一个简化的示例展示如何从零开始构建一个具备搜索和计算技能的Phi-3-Mini智能体。我们会用Python和一些常见的库来实现。3.1 环境准备与基础设置首先确保你的环境已经安装了Phi-3-Mini-128K的推理库。这里以使用transformers库为例。pip install transformers torch然后我们加载模型和分词器from transformers import AutoModelForCausalLM, AutoTokenizer model_name microsoft/Phi-3-mini-128k-instruct tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name, device_mapauto, torch_dtypeauto) # 设置一个简单的对话模板 def format_chat_prompt(messages): formatted_text for message in messages: if message[role] user: formatted_text f|user|\n{message[content]}|end|\n elif message[role] assistant: formatted_text f|assistant|\n{message[content]}|end|\n else: formatted_text f|system|\n{message[content]}|end|\n formatted_text |assistant|\n return formatted_text3.2 定义我们的第一个技能网络搜索为了安全起见我们这里用一个模拟的搜索函数来代替真实的网络API。在实际应用中你可以替换成SerpAPI、Google Custom Search等服务的调用。# 模拟一个简单的网络搜索技能 def skill_web_search(query): 技能网络搜索 功能根据查询词返回模拟的搜索结果。 输入查询关键词字符串 输出搜索结果摘要字符串 # 这里模拟返回固定结果实际应调用搜索API mock_results { AI news: 今日主要AI新闻包括DeepMind发布新算法OpenAI宣布模型升级多家公司推进AI芯片研发。行业焦点集中在多模态应用和效率提升。, weather Beijing: 北京今日晴转多云气温15-25摄氏度南风2-3级。, Python tutorial: Python是一种流行的编程语言以简洁易读著称适合数据分析、人工智能和Web开发。 } # 简单匹配实际应用需更复杂的查询匹配逻辑 for key in mock_results: if key.lower() in query.lower(): return f[网络搜索] 关于 {query} 的结果{mock_results[key]} return f[网络搜索] 未找到关于 {query} 的明确信息。3.3 定义第二个技能计算器同样我们实现一个安全的计算器技能使用ast.literal_eval来避免直接执行危险代码。import ast import operator def skill_calculator(expression): 技能计算器 功能计算一个安全的数学表达式。 输入数学表达式字符串如 2 3 * 4 输出计算结果字符串或错误信息 # 定义一个安全的操作符映射 safe_operators { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.Pow: operator.pow, ast.USub: operator.neg, } def safe_eval(node): if isinstance(node, ast.Constant): # 处理Python 3.8的常量 return node.value elif isinstance(node, ast.Num): # 处理旧版数字 return node.n elif isinstance(node, ast.BinOp): left safe_eval(node.left) right safe_eval(node.right) op_type type(node.op) if op_type in safe_operators: return safe_operators[op_type](left, right) else: raise ValueError(f不支持的运算符: {node.op}) elif isinstance(node, ast.UnaryOp): operand safe_eval(node.operand) op_type type(node.op) if op_type in safe_operators: return safe_operators[op_type](operand) else: raise ValueError(f不支持的运算符: {node.op}) else: raise ValueError(f不支持的表达式类型: {type(node).__name__}) try: tree ast.parse(expression, modeeval) result safe_eval(tree.body) return f[计算器] 表达式 {expression} 的结果是{result} except (ValueError, SyntaxError, ZeroDivisionError) as e: return f[计算器] 计算错误无法计算表达式 {expression}。错误{e}3.4 创建技能注册与调度中心现在我们需要一个中心来管理所有技能并让模型知道它们的存在。class SkillRegistry: def __init__(self): self.skills {} self.skill_descriptions [] def register_skill(self, name, function, description): 注册一个技能 self.skills[name] function self.skill_descriptions.append(f- {name}: {description}) def get_skill(self, name): 获取技能函数 return self.skills.get(name) def list_skills_prompt(self): 生成描述所有技能的系统提示词 skills_text \n.join(self.skill_descriptions) return f你是一个AI助手除了对话你还可以调用以下工具技能来帮助用户 {skills_text} 当用户的问题需要用到这些工具时请在你的回复中明确指出你将调用哪个工具并给出调用所需的参数。格式为 调用技能[技能名称]/调用技能 参数[参数内容]/参数 调用完成后我会将工具的结果提供给你请你结合结果生成最终回答。 3.5 构建智能体交互循环最后我们把所有部分组装起来形成一个简单的交互循环。这个循环会1. 将技能描述和用户问题一起给模型2. 解析模型回复中的技能调用意图3. 执行技能4. 将结果返回给模型生成最终答案。def run_agent_loop(user_query, registry, model, tokenizer, max_history3): 运行智能体交互循环 system_prompt registry.list_skills_prompt() conversation_history [{role: system, content: system_prompt}] conversation_history.append({role: user, content: user_query}) # 第一步让模型规划是否需要调用技能 prompt_text format_chat_prompt(conversation_history) inputs tokenizer(prompt_text, return_tensorspt).to(model.device) outputs model.generate(**inputs, max_new_tokens256, temperature0.1) initial_response tokenizer.decode(outputs[0], skip_special_tokensTrue) # 提取模型的新回复部分 assistant_response initial_response.split(|assistant|\n)[-1].split(|end|)[0].strip() print(f模型初始回复:\n{assistant_response}\n) # 第二步尝试解析技能调用指令这里是一个简单的文本匹配解析 skill_to_call None param_for_skill None if 调用技能 in assistant_response and 参数 in assistant_response: try: skill_to_call assistant_response.split(调用技能)[1].split(/调用技能)[0].strip() param_for_skill assistant_response.split(参数)[1].split(/参数)[0].strip() except IndexError: pass final_answer assistant_response # 第三步如果解析到技能调用则执行 if skill_to_call and skill_to_call in registry.skills: print(f检测到技能调用: {skill_to_call}, 参数: {param_for_skill}) skill_func registry.get_skill(skill_to_call) skill_result skill_func(param_for_skill) print(f技能执行结果: {skill_result}\n) # 第四步将技能结果反馈给模型让它生成最终回答 follow_up_prompt f用户的问题{user_query}\n你之前决定调用技能。技能 {skill_to_call} 返回的结果是{skill_result}\n请根据这个结果生成对用户的最终回答。 conversation_history.append({role: user, content: follow_up_prompt}) prompt_text format_chat_prompt(conversation_history) inputs tokenizer(prompt_text, return_tensorspt).to(model.device) outputs model.generate(**inputs, max_new_tokens512, temperature0.1) final_response tokenizer.decode(outputs[0], skip_special_tokensTrue) final_answer final_response.split(|assistant|\n)[-1].split(|end|)[0].strip() return final_answer # 初始化技能注册中心并注册技能 registry SkillRegistry() registry.register_skill(web_search, skill_web_search, 在互联网上搜索信息。输入应为搜索关键词。) registry.register_skill(calculator, skill_calculator, 计算数学表达式。输入应为如 2 3 * 4 的表达式。) # 运行示例 if __name__ __main__: user_question 今天的AI新闻有什么另外请计算一下(15 7) * 3 等于多少 print(f用户问题: {user_question}\n) answer run_agent_loop(user_question, registry, model, tokenizer) print(f最终回答:\n{answer})运行上面的代码你会看到模型首先分析问题识别出需要调用web_search和calculator技能然后程序执行这些技能并将结果反馈给模型由模型整合成一段连贯、自然的最终回答。这就完成了一个简单的智能体工作流。4. 从Demo到实用进阶思路与优化上面的例子是一个极简的演示。要构建一个实用的智能体还需要考虑更多更强大的规划能力我们的例子依赖模型单次回复中的指令。更成熟的框架如LangChain的Agent、AutoGen会实现多轮“思考-行动-观察”的循环让模型能自主完成包含多个步骤的复杂任务。更规范的技能调用协议使用JSON Schema等标准格式来描述技能让模型以结构化方式如Function Calling调用提高解析的可靠性。丰富的技能生态除了搜索和计算可以集成邮件发送、日历管理、数据分析、代码执行等上百种技能真正成为全能助手。记忆与上下文管理让智能体记住之前的对话和操作结果在长对话中保持连贯性。错误处理与安全性对技能执行失败、网络超时、有害指令等进行妥善处理确保系统稳定安全。5. 总结通过Skills框架为像Phi-3-Mini-128K这样的轻量模型扩展工具调用能力是一个性价比很高的方案。它不需要对模型本身进行昂贵的微调而是通过外部框架赋予模型“行动力”。从简单的搜索计算到自动化的数据分析和报告生成这种模式极大地拓展了模型的应用边界。自己动手实现一个基础版本是理解智能体工作原理的最佳方式。你可以先从一两个核心技能开始比如把模拟搜索换成真实的搜索引擎API或者接入一个数据库查询接口。在这个过程中你会更深刻地体会到模型规划、工具执行、结果整合这一整套流程的精妙与挑战。当你的智能体第一次成功调用外部工具并完成任务时那种感觉就像给一个聪明的头脑装上了灵活的手脚看着它开始自主探索和解决问题这或许就是AI应用开发中最有意思的部分。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。