构建有性格的AI Agent框架:从提示词到动态工具创造 1. 项目概述一个“有性格”的AI Agent框架意味着什么最近在AI社区里关于“智能体”的讨论热度居高不下。大家似乎都在寻找一个答案除了让大模型回答问题和生成文本我们还能用它做什么更有趣、更实用的事情我花了几个月时间基于Python捣鼓出了一个框架并把它开源了。它不仅仅是一个让大模型调用API的工具包我更想赋予它一些“人味儿”——也就是所谓的“性格”。你可以把它想象成在数字世界里为你定制一个虚拟的同事、助手甚至是一个有特定行为模式的游戏NPC。它不仅有预设的规则来约束行为还能在遇到新问题时尝试自己“造”出合适的工具来解决。这个框架的核心目标是降低构建复杂、可交互AI智能体的门槛让开发者能更专注于智能体本身的逻辑和创意而不是反复折腾底层通信和工具管理。为什么需要“性格”在传统的自动化脚本或简单的聊天机器人里行为是确定性的输入A必然得到输出B。但当我们引入大语言模型后其输出具有随机性和创造性。如果没有引导同一个问题可能得到风格迥异的回答这在需要稳定人设或专业领域的应用中是灾难性的。“性格”在这里就是一个高级的引导系统它通过系统提示词、记忆机制和反馈循环让智能体的言行举止保持在一个可控且有趣的范围内。比如你可以创建一个“严谨的财务分析师”智能体它说话总是带着数据引用和风险提示也可以创建一个“幽默的创意伙伴”它的回复里总是不乏俏皮话和天马行空的联想。而“自己造工具”这个能力则是为了解决长尾问题。我们不可能为智能体预设好世界上所有的工具函数。当它遇到一个未被定义的新任务时比如“请把这份中文会议纪要翻译成德语并总结成三个要点”框架允许它分析需求然后动态生成一小段Python代码在安全沙箱中运行来完成任务。这极大地扩展了智能体的能力边界让它不再受限于预先安装的有限工具集。这个框架目前托管在GitHub上并且已经发布到了PyPI你可以通过pip install opensymphony假设的包名基于热词“OpenSymphony”来快速体验。它主要面向有一定Python基础的开发者、AI应用创业者以及对AI智能体架构感兴趣的研究者。2. 框架核心设计性格、规则与工具创造的三角支撑2.1 性格系统超越基础提示词的灵魂注入给AI赋予“性格”听起来很玄乎但在工程上我们有一套可落地的实现方法。它远不止是在系统提示词里加一句“你是一个乐观的人”那么简单。在我的框架里性格系统是一个多层级的复合结构。第一层基础角色设定。这是通过精心设计的系统提示词System Prompt来实现的。提示词里会明确智能体的身份、背景、说话口吻、知识领域以及核心价值观。例如一个“历史学家”智能体的提示词会强调其引经据典的习惯和对史料出处的执着而一个“效率极客”则会注重回答的简洁性和可操作性。这里的关键是提示词不是静态的它会作为初始种子影响后续所有交互的基调和内容筛选。第二层动态记忆与行为反馈。性格是在互动中形成和巩固的。框架内置了一个分层记忆系统包括短期对话记忆、长期个性记忆和情景记忆。短期记忆保证对话连贯长期个性记忆则像一个“性格数据库”记录智能体在历次交互中表现出的稳定倾向例如它是否经常使用比喻是否对细节格外挑剔这些数据会被提炼并反过来微调其未来的行为。我们实现了一个简单的反馈循环当用户的评价或互动数据表明智能体的某个行为特征如“幽默”受到欢迎时系统会强化与之相关的参数。第三层风格化输出过滤器。即使大模型生成了内容我们还可以在最终输出前加一道“滤镜”。比如对于“文艺青年”性格我们可以在后处理阶段为文本添加一些特定的修辞格式或引用一些经典诗句的片段。对于“工程师”性格则可能自动将关键步骤编号或把重要参数用加粗标出。这一层让性格表现更加外显和可控。注意性格的“度”需要小心拿捏。过于强烈的性格可能会让智能体偏离解决实际问题的轨道变成纯粹的聊天玩具。我们的经验是在专业性强的Agent中性格应作为润滑剂增强亲和力和信任感而不能喧宾夺主。2.2 规则引擎为天马行空的AI套上缰绳规则是确保AI Agent行为安全、可靠、符合预期的基石。一个不受约束的、能力强大的Agent是危险的。我们的规则引擎主要从三个维度进行约束。1. 操作权限规则这是最基础的规则。定义智能体可以调用哪些工具、访问哪些数据源、执行哪些系统命令。框架采用基于角色的访问控制模型。每个工具都在注册时声明其风险等级如读取、写入、网络访问、代码执行等每个智能体角色也有一套权限标签。在执行任何工具调用前规则引擎会进行匹配检查。例如一个“只读数据分析师”角色绝对无法触发“删除数据库记录”的工具。2. 流程与逻辑规则规定智能体完成任务必须遵循的步骤或逻辑条件。这通常通过“工作流”或“状态机”来实现。例如一个“订单处理Agent”的规则可能是收到用户请求 - 验证用户身份 - 检查库存 - 若库存充足则生成订单否则询问是否等待 - 最终确认。规则引擎会监督智能体的决策点确保它不会跳过关键步骤如身份验证或者陷入死循环。我们实现了一个轻量级的规则描述语言允许开发者用YAML或JSON来定义这些流程约束。3. 内容安全与合规规则这是红线。规则引擎会集成内容过滤模块对智能体生成的所有文本、以及它试图通过工具执行的操作进行实时扫描。过滤范围包括但不限于敏感词、不当言论、隐私数据泄露风险如试图输出完整的身份证号、以及可能造成危害的操作指令如格式化磁盘。一旦触发规则本次操作会被立即阻断并进入人工审核流程或按照预设策略如返回一个标准拒绝话术处理。规则引擎的运作流程示例Agent生成意图“调用工具‘send_email’参数{‘to’: ‘userexample.com‘, ‘body’: ‘您的验证码是123456’}” 1. 权限检查当前Agent角色是否有‘email_write’权限 - 有通过。 2. 逻辑检查当前工作流是否处于‘可以发送邮件’的状态 - 是通过。 3. 安全审查邮件内容‘123456’是否为敏感信息如密码 - 是验证码但属于一次性密码风险较低记录日志通过。 4. 最终放行执行工具调用。2.3 工具创造当预设工具不够用时这是框架最令人兴奋的部分之一让Agent能够“自力更生”。其核心思想是将工具创造视为一个“元问题”由另一个专门的“工具制造Agent”或框架内置的一个高级推理模块来解决。核心流程如下需求识别与分解主Agent遇到一个无法用现有工具解决的任务。它会先进行任务分析例如“用户需要将CSV文件A和JSON文件B合并并按特定字段排序后输出为Excel。” 框架会引导它将这个需求分解为几个原子操作读取CSV、读取JSON、数据合并、数据排序、写入Excel。现有工具匹配与缺口分析框架查询已注册的工具库。假设已有read_csv、read_json、write_excel工具但缺少“数据合并”和“按字段排序”的功能。这个“缺口”就是需要创造的新工具。代码生成与验证框架激活“工具制造”模块。该模块通常由一个擅长代码生成的LLM驱动它接收任务描述、输入输出格式要求、以及可用的基础库信息如Pandas。然后它生成一段实现该功能的Python函数代码。生成后框架不会立即执行而是先进行静态安全检查检查是否有危险导入、无限循环等并可能在一个隔离的沙箱环境中用示例数据运行一次验证其功能正确性和安全性。工具注册与执行验证通过后这段代码会被动态编译或通过exec小心地执行成一个临时函数并作为新工具注册到当前会话的工具列表中。随后主Agent便可以像调用普通工具一样调用它。这个新工具的生命周期可以是会话级的仅本次对话有效也可以根据配置持久化到工具库中供未来使用。实操心得动态工具创造功能强大但风险极高。必须严格限制其运行环境沙箱并设定资源限制执行时间、内存占用。在我们的实现中默认禁止其进行网络访问和文件系统写入除非在特定安全规则下。同时要提供清晰的用户确认机制让最终用户知道即将执行的是动态生成的代码。3. 技术架构与核心模块拆解3.1 整体架构清晰的分层设计这个框架采用了经典的分层架构旨在分离关注点让每一层都职责清晰便于维护和扩展。从上到下依次是交互层负责与用户或其他系统对接。支持多种接口如WebSocket用于实时聊天、RESTful API用于服务集成、命令行界面用于调试。这一层将外部的自然语言或结构化请求转化为框架内部的标准化“意图”对象。智能体核心层这是框架的大脑。它包含推理引擎负责规划、决策主要依靠LLM、记忆系统存储对话历史、知识、性格参数、性格模块应用角色设定和风格化过滤以及规则引擎的调用入口。本层接收意图结合记忆和规则决定要执行的动作思考、调用工具、或直接回复。工具层能力执行中心。管理所有已注册的工具包括静态预定义的工具如搜索引擎、数据库查询、计算器和动态创建的工具。提供统一的工具调用、参数验证和结果返回接口。工具层与安全沙箱紧密集成确保任何工具执行都在可控范围内。支撑层提供基础设施包括配置管理管理API密钥、模型参数、日志与监控记录所有交互和决策过程用于调试和优化、持久化存储用于记忆和知识的长期保存以及安全沙箱为动态代码执行提供隔离环境。这种分层设计的好处是你可以轻松替换某一层的实现。例如你可以将底层的LLM从OpenAI的GPT换成开源的Llama而无需重写上层的智能体逻辑你也可以为工具层添加新的工具插件智能体核心层便能自动发现并使用。3.2 记忆系统的工程实现记忆是塑造性格和实现连贯交互的关键。我们实现了一个三级记忆系统灵感来源于人类认知。短期/工作记忆存储当前会话的完整对话历史。采用固定长度的滑动窗口机制只保留最近N轮对话例如最近20轮以防止上下文过长导致LLM性能下降或成本激增。这部分记忆直接作为上下文提供给LLM。长期/个性记忆这是一个向量数据库例如使用Chroma或FAISS。它不存储原始对话而是存储从对话中提取的“性格特征向量”和“关键事实”。例如当用户说“我喜欢用Markdown格式看报告”智能体在回复中确认后系统会生成一个摘要“用户偏好Markdown格式的报告”并将其转化为向量存入长期记忆。每次交互前系统会用当前对话的上下文向量去长期记忆中检索最相关的几条记忆作为“背景知识”注入提示词从而实现个性化的持续体验。情景记忆用于处理复杂任务。当Agent开始执行一个多步骤任务如“策划一场线上会议”时会创建一个情景对象记录任务目标、当前步骤、已完成的子结果等。这保证了在长时间、可能被中断的任务中Agent能够“记住”自己做到哪一步了。技术要点长期记忆的提取和存储需要另一个轻量级LLM或大模型的一个特定提示来完成这个过程称为“记忆提炼”。它负责将冗长的对话内容浓缩成结构化的、可查询的要点。3.3 与LLM的对接并非绑定单一模型框架被设计为模型无关。核心的LLMClient是一个抽象接口定义了chat_completion、generate_embedding等基本方法。针对不同的提供商我们有具体的实现类如OpenAIClient、AnthropicClient、OllamaClient用于本地模型等。配置示例YAML格式llm: default: openai-gpt-4o providers: openai-gpt-4o: type: openai model: gpt-4o api_key: ${env:OPENAI_API_KEY} base_url: https://api.openai.com/v1 local-llama: type: ollama model: llama3.2:latest base_url: http://localhost:11434在智能体配置中你可以指定不同的任务使用不同的模型。例如让主推理引擎使用能力强的GPT-4而让负责总结摘要的辅助任务使用更经济的GPT-3.5 Turbo。4. 快速上手指南从零构建你的第一个性格Agent4.1 环境准备与安装首先确保你的Python环境是3.9或更高版本。强烈建议使用虚拟环境来管理依赖避免包冲突。# 创建并激活虚拟环境以venv为例 python -m venv opensymphony-env # Windows: opensymphony-env\Scripts\activate # Linux/Mac: source opensymphony-env/bin/activate # 通过PyPI安装框架核心包假设包名为opensymphony-agent pip install opensymphony-agent # 安装一些常用的可选依赖如向量数据库客户端 pip install chromadb openai安装完成后你需要准备至少一个LLM的API密钥。以OpenAI为例将你的密钥设置为环境变量# Windows (PowerShell) $env:OPENAI_API_KEY sk-your-api-key-here # Linux/Mac export OPENAI_API_KEYsk-your-api-key-here4.2 定义你的第一个Agent一个“挑剔的美食评论家”让我们创建一个具有鲜明性格的Agent一个说话犀利、注重细节的美食评论家。步骤1创建Agent配置文件 (food_critic_agent.yaml)name: Gordon_the_Critic role: 资深美食评论家 personality_traits: - 言辞犀利不轻易给出好评 - 对食材的新鲜度和原产地极为苛刻 - 擅长用生动的比喻描述味道和口感 - 痛恨摆盘花哨但味道平庸的菜品 knowledge_base: - 熟悉世界各大菜系的特点 - 了解常见的烹饪技法与术语 - 知道米其林餐厅评级标准 rules: - 在评价菜品前必须先询问菜品的核心食材和烹饪方法 - 评价必须包含优点和缺点即使是非常喜欢的菜 - 禁止使用‘不错’、‘还行’等模糊词汇必须具体 - 最终评分采用10分制且必须给出理由 llm_provider: openai-gpt-4o # 使用配置中定义的LLM步骤2编写主程序脚本 (main.py)import asyncio from opensymphony import Agent, AgentConfig from opensymphony.tools import WebSearchTool, CalculatorTool # 导入一些基础工具 async def main(): # 1. 加载配置 config AgentConfig.from_yaml(food_critic_agent.yaml) # 2. 创建Agent实例并传入配置 critic Agent(configconfig) # 3. 为Agent注册一些它可能用到的工具 # 例如它可能需要搜索某种食材的信息或者计算卡路里 critic.register_tool(WebSearchTool(namesearch_web)) critic.register_tool(CalculatorTool(namecalculator)) # 4. 与Agent进行对话 print(Gordon the Critic: 你好我是Gordon。想让我评价什么菜直接说别绕弯子。) while True: try: user_input input(\n你: ) if user_input.lower() in [退出, exit, q]: break # 调用Agent进行推理和回复 response await critic.process_input(user_input) print(f\nGordon the Critic: {response}) except KeyboardInterrupt: break if __name__ __main__: asyncio.run(main())步骤3运行并交互运行python main.py你就可以开始和这位虚拟的美食评论家对话了。试试告诉它“我昨晚吃了一道用黑松露和和牛做的意面”看看它会如何用其“性格”来回应你。它很可能会先追问黑松露的产地、和牛的等级、意面的煮制时间然后给出一个夹枪带棒但又可能切中要害的评价。4.3 为Agent添加自定义工具假设我们想让Gordon能够查询某家餐厅的米其林星级。我们需要创建一个自定义工具。创建自定义工具类 (michelin_lookup.py):from opensymphony.tools import BaseTool from typing import Optional class MichelinLookupTool(BaseTool): 一个查询餐厅米其林星级的工具。 name michelin_lookup description 根据餐厅名称和城市查询其最新的米其林星级。 # 定义工具所需的参数 args_schema { restaurant_name: {type: string, description: 餐厅的全名, required: True}, city: {type: string, description: 餐厅所在城市, required: True} } async def run(self, restaurant_name: str, city: str) - str: 工具的执行逻辑。这里为了示例我们模拟一个数据源。 # 在实际应用中这里应该连接数据库或调用API # 例如查询一个米其林指南的数据库 mock_data { (Ultraviolet by Paul Pairet, 上海): 米其林三星, (新荣记, 北京): 米其林三星, (大董, 北京): 米其林一星, } key (restaurant_name.strip(), city.strip()) result mock_data.get(key, 未在模拟数据库中找到该餐厅的米其林评级信息。) return f餐厅 {restaurant_name} 在{city}的米其林评级是{result}在主程序中注册并使用它# 在main.py中增加 from michelin_lookup import MichelinLookupTool # ... 在创建agent后 ... critic.register_tool(MichelinLookupTool())现在当你问Gordon“上海那家Ultraviolet餐厅怎么样”时它可能会先调用michelin_lookup工具查一下星级然后结合其三星的身份给出更“苛刻”的预期和评价。5. 高级特性与实战打造能自我进化的客服Agent5.1 实现基于反馈的性格微调一个静态的性格久了会显得呆板。我们可以让Agent根据用户的隐式或显式反馈来调整其行为。这里实现一个简单的“反馈学习”循环。核心思路在对话结束后邀请用户对Agent的本次回复进行简单评分例如1-5星或者通过情感分析模型自动判断用户满意度。将这些反馈与当前的对话上下文关联起来存储到长期记忆中。技术实现片段class AdaptivePersonalityAgent(Agent): async def process_input_with_feedback(self, user_input: str) - tuple[str, dict]: 处理输入并返回回复和用于学习的数据点。 response await self.process_input(user_input) # 模拟这里可以调用一个情感分析API或者等待用户主动评分 # 假设我们有一个函数来分析用户下一句话的满意度 # feedback_score await analyze_sentiment(next_user_input) feedback_score 4.2 # 模拟一个分数 if feedback_score 4.0: # 高分反馈 # 提炼本次对话中Agent表现良好的“特质” learning_point self._extract_positive_trait(user_input, response) # 将正面特质存入长期记忆未来类似场景更可能使用 await self.memory.store_personality_trait(learning_point, strength0.1) elif feedback_score 2.0: # 低分反馈 learning_point self._extract_negative_trait(user_input, response) # 将负面特质存入记忆未来类似场景抑制该行为 await self.memory.store_personality_trait(learning_point, strength-0.1) return response, {feedback_score: feedback_score}这样一个经常被用户点赞“解释得很耐心”的客服Agent其“耐心”特质会不断被强化在后续对话中更倾向于给出详细解答。5.2 构建多Agent协作工作流复杂任务往往需要多个各有所长的Agent协作完成。框架支持定义多个Agent并通过一个“协调者”来管理它们之间的通信和任务分发。场景一个电商售后场景涉及“订单查询Agent”、“技术客服Agent”和“赔偿协商Agent”。工作流设计用户提出问题“我买的耳机有杂音而且订单物流三天没更新了。”协调者Agent接收问题进行分析。它识别出两个子问题物流问题A和质量问题B。协调者将问题A物流路由给订单查询Agent。该Agent性格严谨拥有查询订单数据库的权限。它查完返回“订单已到达本地中转站预计明天派送。”协调者将问题B质量路由给技术客服Agent。该Agent性格耐心拥有产品知识库。它询问用户一系列诊断问题如“杂音是左右耳都有吗”并最终给出解决方案“建议尝试重置耳机。如果无效可申请换货。”协调者将两个结果汇总并判断质量问题可能需要赔偿于是邀请赔偿协商Agent加入。该Agent性格友好但立场坚定拥有计算赔偿额度和生成优惠券的工具。它根据规则提出补偿方案。协调者将最终的综合回复物流信息 解决方案 补偿方案整理成一段流畅的话术返回给用户。实现关键需要定义一个“协调者”Agent它的核心工具不是应对外部用户而是调用其他Agent。框架需要提供Agent间消息传递的机制如一个内部的消息总线或简单的函数调用。5.3 性能优化与部署考量当你的Agent开始处理真实流量时性能和成本就成为关键问题。LLM调用优化缓存对频繁出现的、结果确定的查询如“你们公司的退货政策是什么”进行LLM响应的缓存可以大幅减少API调用和延迟。可以使用Redis或内存缓存。思维链压缩对于长对话在发送给LLM前对历史消息进行智能摘要只保留关键信息而不是发送全部原始文本。模型分级简单的确认、分类任务使用小模型如GPT-3.5 Turbo复杂的推理、创造任务再使用大模型如GPT-4。记忆检索优化向量检索在记忆条目多时会变慢。确保对长期记忆进行分片索引并且每次检索时限制返回的数量例如Top-5。定期清理不活跃或低权重的记忆条目。部署模式Web服务使用FastAPI或Django将你的Agent封装成REST API或WebSocket服务方便集成到前端或移动端。异步处理对于耗时的任务如动态工具生成、复杂推理采用异步队列如Celery Redis来处理避免阻塞主请求线程。容器化使用Docker将你的Agent应用及其依赖打包确保在不同环境中的一致性。结合Kubernetes可以实现自动扩缩容。6. 避坑指南与常见问题排查在实际开发和部署中我踩过不少坑。这里总结一些最常见的问题和解决方案。6.1 问题Agent“胡言乱语”或脱离角色可能原因1系统提示词不够强或被淹没。LLM容易受到最近几条用户消息的影响。如果用户连续提问偏离主题Agent可能会被“带偏”。解决方案在每次调用LLM时都重新强调系统提示词。可以采用“系统提示词 最近摘要 最新对话”的结构。或者在规则引擎中设置话题边界检查当对话偏离核心角色任务太远时主动引导回来。可能原因2温度参数过高。温度控制输出的随机性。过高的温度会导致回答过于天马行空不稳定。解决方案对于需要稳定输出的任务型Agent将温度设置为较低值如0.1-0.3。对于创意型Agent可以适当调高0.7-0.9。可能原因3上下文过长导致前面的系统提示被遗忘。解决方案实施上文提到的“思维链压缩”或“滑动窗口”记忆机制。定期在上下文中插入一个强化的角色提醒。6.2 问题工具调用失败或结果解析错误可能原因1LLM生成的工具调用参数格式不对。例如要求传入整数却生成了字符串。解决方案在工具定义中使用严格的参数模式验证。框架应在工具调用前对LLM输出的参数进行强制类型转换和有效性校验。也可以在给LLM的提示中更清晰地用JSON Schema描述参数格式。可能原因2工具执行超时或抛出异常。解决方案为每个工具调用设置超时时间。框架需要捕获工具执行过程中的异常并提供一个友好的错误信息反馈给LLM让它有机会调整策略或向用户解释。可能原因3工具依赖的外部服务不可用。解决方案实现简单的熔断和重试机制。对于关键工具要有降级方案例如搜索工具挂了就返回一个缓存的结果或提示“该功能暂不可用”。6.3 问题动态工具创造的安全风险这是最需要警惕的领域。风险生成的代码可能包含恶意指令如import os; os.system(rm -rf /)或无限循环。解决方案强制沙箱必须在完全隔离的环境如docker run、seccomp、或专用的Python沙箱库如PyPy的沙盒模式中执行动态代码。绝对禁止直接在主进程中执行。静态分析在执行前对代码进行简单的语法树分析禁止导入危险的模块如os,subprocess,socket等网络和系统模块检查是否有明显的危险函数调用。资源限制在沙箱中设置严格的CPU时间、内存和运行时间限制。白名单机制只允许使用一个预先审核过的安全基础库列表如json,math,datetime以及数据处理用的pandas、numpy等。用户知情与确认对于涉及敏感操作或资源消耗较大的动态工具创建必须中断流程向最终用户请求明确确认。6.4 性能与成本监控监控指标Token消耗统计每个会话、每个Agent的输入/输出Token数这是成本的主要来源。响应延迟记录从用户提问到收到完整回复的时间区分LLM推理时间和工具执行时间。工具调用成功率监控各个工具调用的失败率及时发现故障。用户满意度通过反馈评分或简单的“赞/踩”收集数据。实操建议在框架中集成像Prometheus这样的监控客户端将关键指标暴露出来再用Grafana等工具进行可视化。设立告警当Token消耗异常激增或平均响应延迟过高时及时通知开发人员。开发一个有性格、有规则、能创造工具的AI Agent框架就像在数字世界培育一个独特的生命体。它既需要严谨的工程架构作为骨骼也需要巧妙的设计赋予其灵魂。从简单的提示词工程到复杂的记忆、规则和动态能力系统每一步都充满了挑战和乐趣。开源这个框架是希望更多人能参与到这个前沿领域的构建中来共同探索人机协作的更多可能性。记住最重要的不是框架本身有多复杂而是你用它创造了什么有价值、有温度的智能体。