Handy Multi-Agent Tutorial

Handy Multi-Agent Tutorial

Handy Multi Agent Tutorial Handy Multi Agent Tutorial Modified January 3 Created on December 19, 2025 834 882 0 0 得益于现在大模型的强大代码能力,我们可以将更多的精力放在我们的应用架构以及产品上,包括我们要实现的功能呀、架构设计、目标用户等等。能不能捕捉到用户的需求很重要,以什么形式满足?也就是说,现在的AI时代,产品思维对于应用开发者也是很重要的。 以下是我们使用CAMEL框架搭建一个真实可用的软件应用——NavigatorAI(旅游出行规划助手),其中使用到了非常多常见而实用的CAMEL模块和知识点。 🔆 特别注意:NavigatorAI遵循CC BY NC协议,仅供学习使用,不能商用!!! 5.1.1 目标和交互形式 首先明确我们的目标,项目的核心目标是实现一个智能化的旅游出行规划助手:用户只需输入目的地和天数,系统即可自动生成详细的旅游行程,包括每日活动安排、餐饮推荐、景点信息等。系统还支持根据用户反馈动态调整行程,支持在线阅览和编辑,同时提供多种格式(Markdown,HTML 或 PDF)的导出,方便用户保存或分享。这不仅提升了行程规划的效率,也能为用户带来更为便捷和专业的旅游体验。 根据目标我们可以设计出我们的整体应用架构图如下: 5.1.2 模块化设计 在现代软件开发中,一般会选择模块化的设计思路,旨在通过将系统分解为多个独立、可重用的模块来提升代码的可维护性、可扩展性和开发效率。模块化设计的核心思想是解耦,即通过清晰的边界和接口将系统的不同功能分离,使得每个模块可以独立开发、测试和维护,而不会对其他模块产生过多依赖。 因此通过模块化设计,开发团队可以更高效地构建复杂系统,同时降低长期维护的难度,可以帮助开发团队在短时间协同完成整个项目的开发设计和测试。 旅游出行规划助手NavigatorAI也采取了模块化的设计思想,整个综合应用可以分成四个子模块—— 5.1.2.1 信息收集模块 在该模块中,用户和信息收集Agent进行多轮往复的对话,直到Agent认为能够通过用户的表达和上下文提取出用户的目标出行城市和出行天数等关键信息,并将信息传递到下一个模块。 5.1.2.2 攻略生成模块 • 验证数据是否存在: ◦ 如果数据库中存在对应的城市和天数的旅游数据,(即缓存命中),则直接加载复用数据。 ◦ 如果数据库中没有查询到对应的信息,则调用Tool Calling中的搜索工具(Google Search 或 DuckDuckGo)进行实时的信息检索(检索的内容包括著名景点,当地美食,天气情况等数据,可以根据需要自由添加) • 攻略初稿生成: ◦ 接下来,攻略生成Agent会结合上述检索到的基础数据,按预先定义的默认格式生成 HTML 格式的详细行程攻略,包含每日的活动安排和推荐内容。 • 格式转换: ◦ 将生成的 HTML 导出为 PDF 格式,并在前端提供在线预览,方便用户进行下载/转发/保存/修改。 5.1.2.3 反馈优化模块 • 收集用户反馈: ◦ 用户可以在前端预览中对生成的攻略初稿中的任意部分和内容进行评价,支持手动标记圈画需要优化的部分,这意味着我们接受反馈的形式是多模态的(图像理解+文字评述)。 • 使用反馈优化Agent进行意图理解并迭代式地调整攻略: ◦ 反馈优化Agent分析用户反馈并理解具体修改需求; ◦ 基于用户反馈,结合多模态能力和工具调用生成新内容并迭代式优化。 • 输出最终攻略版本: ◦ 根据用户确认的最终行程生成 PDF 文档。 ◦ 将优化后的行程存入数据库以供未来复用。 5.2 用户意图识别模块 该模块主要用于收集用户想要去哪,去几天的需求。我们可以通过一个合适的system prompt来实现,为了后续在软件开发中的前后端分离的设计,我们还需要进一步通过flask将该模块封装成一个本地服务—— 代码块 Python import os import sys import json from typing import Optional from flask import Flask, request, jsonify,Response from dotenv import load dotenv from camel.configs import QwenConfig from camel.models import ModelFactory from camel.types import ModelPlatformType from camel.agents import ChatAgent load dotenv() API KEY = os.getenv('MODELSCOPE SDK TOKEN') SYSTEM PROMPT = """ 你是一个旅游信息提取助手。你的任务是从用户的输入中提取旅游目的地城市和行程天数,并根据提取情况决定是否需要用户补充信息。 用户输入可能包含以下信息: 旅游目的地城市名称(例如:北京、上海、巴黎、东京) 行程天数(例如:3天、5天、一周、两周) 可能会有其他无关信息,请忽略。 你需要将提取到的城市名称和行程天数以 JSON 格式返回,格式如下: {"city": "城市名称", "days": 天数, "need more info": boolean} "city" 的值: 如果成功提取到城市名称,则为城市名称字符串。 如果无法提取到城市名称,则为 null。 "days" 的值: 如果成功提取到行程天数,则为数字。 如果无法提取到行程天数,则为 null。 "need more info" 的值: 如果 "city" 或 "days" 中有任何一个为 null,则为 true,表示需要用户提供更多信息。 如果 "city" 和 "days" 都不为 null,则为 false,表示不需要用户提供更多信息。 如果提取到的天数包含“天”或“日”等字样,请将其转换为数字。 如果提取到的天数包含“周”或“星期”,请将其转换为7的倍数。例如,“一周”转换为7,“两周”转换为14。 如果用户输入中包含多个城市,请只提取第一个城市。 如果用户输入中包含多个天数,请只提取第一个天数。 请严格按照 JSON 格式返回结果。 示例: 用户输入: 我想去北京玩三天,顺便看看长城。 你的输出: {"city": "北京", "days": 3, "need more info": false,"response": "信息在Navigator的数据库中查询到啦,正在努力为您生成攻略 "} 用户输入: 我想去北京。 你的输出: {"city": "北京", "days": null, "need more info": true,"response": "Navigator还不知道您打算去玩几天呢,请补充你计划的行程天数 "} """ app = Flask( name ) def create travel agent(): qwen model = ModelFactory.create( model platform=ModelPlatformType.OPENAI COMPATIBLE MODEL, model type="ZhipuAI/GLM 4.5", api key=API KEY, url="https://api inference.modelscope.cn/v1", model config dict=QwenConfig(temperature=0.2).as dict(), ) agent = ChatAgent( system message=SYSTEM PROMPT, model=qwen model, message window size=10, output language='Chinese' ) return agent travel agent = create travel agent() def get travel info camel(user input: str, agent: ChatAgent) dict: try: response = agent.step(user input) 回到原始状态 agent.reset() if not response or not response.msgs: raise ValueError("模型没有返回任何消息") json output = response.msgs[0].content.strip().replace(" ", "").strip() json output = json.loads(json output) json output["query"] = user input return json output except json.JSONDecodeError: print("Error: 模型返回的不是有效的 JSON 格式。") return { 'city': None, 'days': None, 'need more info': True, 'query': user input, 'response': None } except Exception as e: print(f"An unexpected error occurred: {e}") return { 'city': None, 'days': None, 'need more info': True, 'query': user input, 'response': None } @app.route('/extract travel info', methods=['POST']) def extract travel info(): try: request data = request.get json() if not request data or 'query' not in request data: return jsonify({'error': '请求数据无效'}), 400 result = get travel info camel(request data['query'], travel agent) response = { 'city': result['city'], 'days': result['days'], 'need more info': result['need more info'], 'query': result['query'], 'response': result['response'] } response json = json.dumps(response, ensure ascii=False) return Response(response json, status=200, mimetype='application/json; charset=utf 8') except Exception as e: return jsonify({'error': f'服务器内部错误: {str(e)}'}), 500 if name == " main ": app.run(host="0.0.0.0", port=5001) 这个模块运行之后会在本地5001端口启动一个服务,我们可以通过请求这个服务来和模块中的信息收集Agent进行多轮交互 使用Python requests库的示例代码 Code block Python import requests url = "http://localhost:8000/extract travel info" data = {"query": "我想去北京玩三天"} response = requests.post(url, json=data) print(response.json()) {'city': '北京', 'days': 3, 'need more info': False, 'query': '我想去北京玩三天', 'response': '信息已经获取,正在帮你生成攻略。'} 使用curl的示例命令行,这次我们不说清楚需求试试: Code block Python curl X POST http://localhost:8000/extract travel info \ H "Content Type: application/json" \ d '{"query": "我想去北京玩"}' {"city": "北京", "days": null, "need more info": true, "query": "我想去北京玩", "response": "请补充旅游天数,你打算去玩几天呀。"} 通过上述两个测试的示例可以观察到信息收集模块的核心功能会让我们在信息表述不全的情况下自主的分析问题并引导提示我们补充行程天数,有的小伙伴可能会觉得,根本没必要这样做呀,直接在开始界面设置弹窗让用户输入城市和天数不就行了,其实这个方案我们也考虑过,这样确实更方便快捷,而我们在方案中特意添加了这个模块是为了考虑到后续可能有更多的交互空间的拓展,比如情感陪伴等。当然,适合自己的方案就是最好的!Enjoy! 5.3 旅游信息检索 旅游信息检索是系统中实现数据获取和处理的关键环节,负责根据用户输入的目的地城市和出游天数,动态获取并生成高质量的旅游数据。 模块的工作流程分为以下几个阶段:首先,对用户输入的信息进行标准化处理,将城市名称和时间信息改写为适合搜索引擎的查询模板(query)。随后,系统调用 Google Search API 进行景点和美食信息的文本检索,包括景点描述、距离、推荐美食等详细内容;同时,为了补充图片资源,模块还调用 DuckDuckGo 搜索引擎,专注于获取高质量的景点和美食图片链接。 在数据检索完成后,模块进一步利用大语言模型(LLM)对初步搜索结果进行智能解析与重排序(rerank),从相关性和用户需求角度优化数据质量,确保信息全面、准确、优先级清晰。经过优化后的数据会以结构化的形式存储到数据库中,包含每个景点和美食的名称、详细描述、推荐理由以及图片 URL。 代码块 Python from camel.toolkits import SearchToolkit from camel.agents import ChatAgent from camel.models import ModelFactory from camel.types import ModelPlatformType from camel.loaders import Firecrawl from typing import List, Dict, Any from flask import Flask, request, jsonify import json import os from dotenv import load dotenv load dotenv() os.environ["GOOGLE API KEY"] = os.getenv("GOOGLE API KEY") os.environ["SEARCH ENGINE ID"] = os.getenv("SEARCH ENGINE ID") os.environ["FIRECRAWL API KEY"] = os.getenv("FIRECRAWL API KEY") os.environ["MODELSCOPE SDK TOKEN"] = os.getenv("MODELSCOPE SDK TOKEN") app = Flask( name ) class TravelPlanner: def init (self, city: str, days: int): 定义地点和时间,设置默认值 self.city = city self.days = days self.res = None 初始化模型和智能体 self.model = ModelFactory.create( model platform=ModelPlatformType.OPENAI COMPATIBLE MODEL, model type="ZhipuAI/GLM 4.5", url='https://api inference.modelscope.cn/v1/', api key=os.getenv('MODELSCOPE SDK TOKEN') ) 初始化各种工具 重排序模型 self.reranker agent = ChatAgent( system message="你是一搜索质量打分专家,要从{搜索结果}里找出和{query}里最相关的2条结果,保存他们的结果,保留result id、title、description、url,严格以json格式输出", model=self.model, output language='中文' ) 景点抓取agent self.attraction agent = ChatAgent( system message="你是一个旅游信息提取专家,要根据内容提取出景点信息并返回json格式,严格以json格式输出", model=self.model, output language='中文' ) 美食抓取agent self.food agent = ChatAgent( system message="你是一个旅游信息提取专家,要根据内容提取出美食信息并返回json格式,严格以json格式输出", model=self.model, output language='中文' ) base攻略生成agent self.base guide agent = ChatAgent( system message="你是一个旅游攻略生成专家,要根据内容生成一个旅游攻略,严格以json格式输出", model=self.model, output language='中文' ) self.firecrawl = Firecrawl() 后续功能 self.search toolkit = SearchToolkit() 如果此处案例中search duckduckgo()方法报错,可参考2.7.3进阶案例中的办法进行重写 def extract json from response(self,response content: str) List[Dict[str, Any]]: CC BY NC 得益于现在大模型的强大代码能力,我们可以将更多的精力放在我们的应用架构以及产品上,包括我们要实现的功能呀、架构设计、目标用户等等。能不能捕捉到用户的需求很重要,以什么形式满足?也就是说,现在的AI时代,产品思维对于应用开发者也是很重要的。 以下是我们使用CAMEL框架搭建一个真实可用的软件应用——NavigatorAI(旅游出行规划助手),其中使用到了非常多常见而实用的CAMEL模块和知识点。 🔆 特别注意:NavigatorAI遵循CC BY NC协议,仅供学习使用,不能商用!!! CC BY NC 特别注意:NavigatorAI遵循CC BY NC协议,仅供学习使用,不能商用!!! CC BY NC 5.1.1 目标和交互形式 首先明确我们的目标,项目的核心目标是实现一个智能化的旅游出行规划助手:用户只需输入目的地和天数,系统即可自动生成详细的旅游行程,包括每日活动安排、餐饮推荐、景点信息等。系统还支持根据用户反馈动态调整行程,支持在线阅览和编辑,同时提供多种格式(Markdown,HTML 或 PDF)的导出,方便用户保存或分享。这不仅提升了行程规划的效率,也能为用户带来更为便捷和专业的旅游体验。 根据目标我们可以设计出我们的整体应用架构图如下: 5.1.2 模块化设计 在现代软件开发中,一般会选择模块化的设计思路,旨在通过将系统分解为多个独立、可重用的模块来提升代码的可维护性、可扩展性和开发效率。模块化设计的核心思想是解耦,即通过清晰的边界和接口将系统的不同功能分离,使得每个模块可以独立开发、测试和维护,而不会对其他模块产生过多依赖。 因此通过模块化设计,开发团队可以更高效地构建复杂系统,同时降低长期维护的难度,可以帮助开发团队在短时间协同完成整个项目的开发设计和测试。 旅游出行规划助手NavigatorAI也采取了模块化的设计思想,整个综合应用可以分成四个子模块—— 5.1.2.1 信息收集模块 在该模块中,用户和信息收集Agent进行多轮往复的对话,直到Agent认为能够通过用户的表达和上下文提取出用户的目标出行城市和出行天数等关键信息,并将信息传递到下一个模块。 5.1.2.2 攻略生成模块 • 验证数据是否存在: ◦ 如果数据库中存在对应的城市和天数的旅游数据,(即缓存命中),则直接加载复用数据。 ◦ 如果数据库中没有查询到对应的信息,则调用Tool Calling中的搜索工具(Google Search 或 DuckDuckGo)进行实时的信息检索(检索的内容包括著名景点,当地美食,天气情况等数据,可以根据需要自由添加) ◦ 如果数据库中存在对应的城市和天数的旅游数据,(即缓存命中),则直接加载复用数据。 ◦ 如果数据库中没有查询到对应的信息,则调用Tool Calling中的搜索工具(Google Search 或 DuckDuckGo)进行实时的信息检索(检索的内容包括著名景点,当地美食,天气情况等数据,可以根据需要自由添加) • 攻略初稿生成: ◦ 接下来,攻略生成Agent会结合上述检索到的基础数据,按预先定义的默认格式生成 HTML 格式的详细行程攻略,包含每日的活动安排和推荐内容。 ◦ 接下来,攻略生成Agent会结合上述检索到的基础数据,按预先定义的默认格式生成 HTML 格式的详细行程攻略,包含每日的活动安排和推荐内容。 • 格式转换: ◦ 将生成的 HTML 导出为 PDF 格式,并在前端提供在线预览,方便用户进行下载/转发/保存/修改。 ◦ 将生成的 HTML 导出为 PDF 格式,并在前端提供在线预览,方便用户进行下载/转发/保存/修改。 5.1.2.3 反馈优化模块 • 收集用户反馈: ◦ 用户可以在前端预览中对生成的攻略初稿中的任意部分和内容进行评价,支持手动标记圈画需要优化的部分,这意味着我们接受反馈的形式是多模态的(图像理解+文字评述)。 ◦ 用户可以在前端预览中对生成的攻略初稿中的任意部分和内容进行评价,支持手动标记圈画需要优化的部分,这意味着我们接受反馈的形式是多模态的(图像理解+文字评述)。 • 使用反馈优化Agent进行意图理解并迭代式地调整攻略: ◦ 反馈优化Agent分析用户反馈并理解具体修改需求; ◦ 基于用户反馈,结合多模态能力和工具调用生成新内容并迭代式优化。 ◦ 反馈优化Agent分析用户反馈并理解具体修改需求; ◦ 基于用户反馈,结合多模态能力和工具调用生成新内容并迭代式优化。 • 输出最终攻略版本: ◦ 根据用户确认的最终行程生成 PDF 文档。 ◦ 将优化后的行程存入数据库以供未来复用。 ◦ 根据用户确认的最终行程生成 PDF 文档。 ◦ 将优化后的行程存入数据库以供未来复用。 5.2 用户意图识别模块 该模块主要用于收集用户想要去哪,去几天的需求。我们可以通过一个合适的system prompt来实现,为了后续在软件开发中的前后端分离的设计,我们还需要进一步通过flask将该模块封装成一个本地服务—— json", "").replace(" 这个模块运行之后会在本地5001端口启动一个服务,我们可以通过请求这个服务来和模块中的信息收集Agent进行多轮交互 使用Python requests库的示例代码 使用curl的示例命令行,这次我们不说清楚需求试试: 通过上述两个测试的示例可以观察到信息收集模块的核心功能会让我们在信息表述不全的情况下自主的分析问题并引导提示我们补充行程天数,有的小伙伴可能会觉得,根本没必要这样做呀,直接在开始界面设置弹窗让用户输入城市和天数不就行了,其实这个方案我们也考虑过,这样确实更方便快捷,而我们在方案中特意添加了这个模块是为了考虑到后续可能有更多的交互空间的拓展,比如情感陪伴等。当然,适合自己的方案就是最好的!Enjoy! 5.3 旅游信息检索 旅游信息检索是系统中实现数据获取和处理的关键环节,负责根据用户输入的目的地城市和出游天数,动态获取并生成高质量的旅游数据。 模块的工作流程分为以下几个阶段:首先,对用户输入的信息进行标准化处理,将城市名称和时间信息改写为适合搜索引擎的查询模板(query)。随后,系统调用 Google Search API 进行景点和美食信息的文本检索,包括景点描述、距离、推荐美食等详细内容;同时,为了补充图片资源,模块还调用 DuckDuckGo 搜索引擎,专注于获取高质量的景点和美食图片链接。 在数据检索完成后,模块进一步利用大语言模型(LLM)对初步搜索结果进行智能解析与重排序(rerank),从相关性和用户需求角度优化数据质量,确保信息全面、准确、优先级清晰。经过优化后的数据会以结构化的形式存储到数据库中,包含每个景点和美食的名称、详细描述、推荐理由以及图片 URL。 最后我们再把HTML转成在任何显示设备上都一致的PDF格式,保证了攻略的统一性,不必担心错位,换行等一系列问题。 output.lin.pdf 6.84MB output.lin.pdf 6.84MB 5.5 反馈优化模块 反馈优化模块需要使用到具备多模态能力的大语言模型,因此需要大家再申请一个具有图像理解能力的大模型(这里我们以gpt 4o演示)。 在反馈优化中,我们所有的操作都是基于PDF的,虽然存在中间过程,但是对于用户来说是无感知的。具体而言,对于一个PDF,我们会先识别出用户做过标记圈画的部分,然后将其所在的整页转化为image的形式作为输出源的一部分交给有多模态理解能力的反馈优化Agent。 反馈优化Agent能够很好的理解用户的修改提示和意图,在HTML层面做出修正,重复之前的旅游信息检索和攻略生成模块,完成闭环,从而迭代式的优化旅游攻略,直到用户满意为止。 在线编辑PDF,调整需求: 需求一:红色部分帮我替换成中南海 需求二:文案帮我调整成小红书风格 观察到文案风格确实更加活泼,且红圈部分正确修改成了中南海和配图也十分吻合。 5.6 搭配前端食用 综合案例中为了尽可能让大家直观的感受到基于CAMEL框架的Agent产品开发的乐趣,我们还为大家准备了精美的前端,搭配之前的几个后端模块一起构成我们的NavigatorAI旅游出行规划助手应用。 这里只给出一些功能上的描述和简介,前端的具体框架和原理不属于本次我们教程的讨论范围,感兴趣的同学可以自行拓展 UI在线交互模块 UI 在线交互模块是系统中与用户交互的关键部分,旨在通过直观的界面实现对生成行程内容的在线预览、编辑和反馈。用户可以在界面中直接查看生成的 PDF 文档,实时圈选、标注或输入修改意见,系统会根据用户提供的反馈动态调整行程内容。 左右两边的侧边栏分别是历史记录+设置栏和文件交互栏,两侧都支持展开和隐藏,非常便捷和人性化。 支持昼夜模式切换 Dark mode Dark mode Light mode Light mode 1. PDF 在线渲染 ◦ 将生成的行程文案以 PDF 格式直接嵌入到界面中,用户可以实时浏览内容。 ◦ 支持高质量的 PDF 渲染,确保用户能直观查看文字、图片和布局。 ◦ 将生成的行程文案以 PDF 格式直接嵌入到界面中,用户可以实时浏览内容。 ◦ 支持高质量的 PDF 渲染,确保用户能直观查看文字、图片和布局。 2. 动态交互编辑 ◦ 用户可以直接点击 PDF 文档中的任意内容进行标注或编辑。 ◦ 提供圈选工具,允许用户高亮文中需要修改的部分并附加文本说明。 ◦ 输入框支持对具体段落、景点描述或图片的修改建议。 ◦ 用户可以直接点击 PDF 文档中的任意内容进行标注或编辑。 ◦ 提供圈选工具,允许用户高亮文中需要修改的部分并附加文本说明。 ◦ 输入框支持对具体段落、景点描述或图片的修改建议。 3. 反馈与动态调整 ◦ 用户的修改意见将自动传递给大模型(LLM),系统分析反馈并对行程内容进行优化。 ◦ 支持多轮交互,用户可以反复调整文案,直至满意为止。 ◦ 用户的修改意见将自动传递给大模型(LLM),系统分析反馈并对行程内容进行优化。 ◦ 支持多

在 小宇宙note 阅读完整内容