用 Chainlit 构建多智能体聊天应用:multi_agent_orchestrator 搭配 Bedrock 与 Ollama 的实战指南
用 Chainlit 构建多智能体聊天应用multi_agent_orchestrator 搭配 Bedrock 与 Ollama 的实战指南【免费下载链接】agent-squadFlexible and powerful framework for managing multiple AI agents and handling complex conversations项目地址: https://gitcode.com/GitHub_Trending/mu/agent-squad本指南基于开源仓库agent-squad中的examples/chat-chainlit-app示例完整讲解如何用 Chainlit说明此处仅指 Python 聊天 UI 框架 Chainlit非仓库内部模块与multi_agent_orchestrator编排框架搭建一个支持智能路由、流式输出与多轮对话的多智能体聊天应用。读完本文你将掌握从环境准备、依赖安装、应用启动到核心代码拆解的全流程并能把一个同时跑在 Amazon Bedrock云上 Agent与 Ollama本地模型之上的三 Agent 应用完整跑通。该示例同时展示了本仓库 Python 版编排框架位于 python/src/multi_agent_orchestrator的典型用法用 Bedrock 上的 Claude 模型做意图分类将用户问题路由到技术 Agent旅行 Agent健康 Agent之一其中健康 Agent 由本地 Ollama 的 Llama 3.1 模型驱动形成云 本地混合的多 Agent 体系。示例应用概览三个 Agent 与一个分类器在动手前先看清这个示例要解决什么问题。应用启动后用户输入任意一句话系统会先经过一个分类器判断意图再把请求交给最合适的 Agent 处理组件名称底层模型运行位置分类器BedrockClassifieranthropic.claude-3-haiku-20240307-v1:0Amazon BedrockAgentTech Agent技术anthropic.claude-3-sonnet-20240229-v1:0Amazon BedrockAgentTravel Agent旅行anthropic.claude-3-sonnet-20240229-v1:0Amazon BedrockAgentHealth Agent健康llama3.1:latest本地 Ollama三个 Agent 的职责描述定义在 examples/chat-chainlit-app/agents.py 中分类器正是依赖这些描述来做意图匹配详见后文分类器的工作原理一节。而示例给出的四类测试问题正好覆盖了三种 Agent 及多轮追问场景问 Seattle 值得去的地方 → 应路由到Travel AgentBedrock问 Seattle 有哪些科技公司 → 应路由到Tech AgentBedrock问 Seattle 什么花粉导致过敏 → 应路由到Health Agent本地 Ollama针对旅行 Agent 的上一轮回答继续追问 → 应保持在同一 Agent 继续多轮对话。环境准备Prerequisites按照 examples/chat-chainlit-app/README.md 的要求运行该应用需要满足以下前置条件Python 3.7 及以上版本且pipPython 包安装器可用本地已安装并运行 Ollama且已拉取 ollamaAgent.py 中指定的模型llama3.1:latest可通过ollama pull llama3.1:latest提前下载AWS 凭证与 Bedrock 模型访问权限由于分类器与两个 Bedrock Agent 都调用bedrock-runtime需要在本机配置好 AWS 凭证如环境变量AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_REGION或使用~/.aws/credentials。从源码看bedrock_classifier.py 在未显式传入region时会读取AWS_REGION环境变量bedrock_llm_agent.py 同样如此同时请确保你的账号已开通上述 Claude 3 Haiku / Sonnet 模型的调用权限。安装依赖与启动应用README 给出的安装与运行步骤如下本文在保留原步骤的基础上补充了关键说明。1. 克隆仓库如尚未完成git clone repository-url cd repository-directory2. 创建并激活虚拟环境推荐python -m venv venv激活虚拟环境Windowsvenv\Scripts\activatemacOS / Linuxsource venv/bin/activate3. 安装依赖进入示例目录并安装依赖pip install -r requirements.txtexamples/chat-chainlit-app/requirements.txt 中锁定的依赖如下依赖包版本作用chainlit1.3.2聊天应用前端 UI 与消息流框架multi_agent_orchestrator0.1.2本仓库 Python 版多 Agent 编排框架PyPI 包ollama0.3.3调用本地 Ollama 服务的 Python 客户端pydantic2.10.1数据校验Chainlit 运行时依赖注意multi_agent_orchestrator0.1.2是从 PyPI 安装的发布包与仓库内 python 目录下的源码对应。如果你希望直接使用仓库源码也可以将python/目录加入PYTHONPATH或按 python/README.md 的方式安装。4. 运行应用chainlit run app.py -w其中-wwatch参数表示启用热重载修改代码后应用自动重启非常适合开发调试。应用启动后Chainlit 会在本地开启一个 Web 聊天界面默认端口8000直接在浏览器中打开即可对话。5. 其他注意事项README 提醒确保multi_agent_orchestrator或其他组件所需的环境变量、配置文件已正确设置主要指 AWS 凭证见上文如果安装包时遇到问题请确认 Python 与 pip 版本为最新并优先在干净的虚拟环境中安装。核心代码拆解app.py 中的编排器初始化应用入口 examples/chat-chainlit-app/app.py 是理解整个编排流程的关键。它依次完成了四件事初始化分类器、初始化编排器、注册 Agent、绑定 Chainlit 事件。分类器初始化custom_bedrock_classifier BedrockClassifier(BedrockClassifierOptions( model_idanthropic.claude-3-haiku-20240307-v1:0, inference_config{ maxTokens: 500, temperature: 0.7, topP: 0.9 } ))这里用 Haiku 模型作为分类器比 Sonnet 更便宜、更快并显式配置了采样参数。对照 bedrock_classifier.py 的源码inference_config各字段的含义与默认值为参数示例中的值源码默认值说明maxTokens5001000生成的最大 token 数temperature0.70.0采样温度越高越随机分类任务通常建议低值以保持确定性topP0.90.9核采样概率阈值stopSequences未设置[]停止序列列表model_id未指定时默认使用anthropic.claude-3-5-sonnet-20240620-v1:0该常量定义在 types/types.py。编排器初始化与配置项说明orchestrator MultiAgentOrchestrator(optionsOrchestratorConfig( LOG_AGENT_CHATTrue, LOG_CLASSIFIER_CHATTrue, LOG_CLASSIFIER_RAW_OUTPUTTrue, LOG_CLASSIFIER_OUTPUTTrue, LOG_EXECUTION_TIMESTrue, MAX_RETRIES3, USE_DEFAULT_AGENT_IF_NONE_IDENTIFIEDFalse, MAX_MESSAGE_PAIRS_PER_AGENT10 ), classifiercustom_bedrock_classifier )OrchestratorConfig的全部字段及源码默认值定义在 types/types.py 中逐一说明如下配置项示例值源码默认值作用LOG_AGENT_CHATTrueFalse是否打印每个 Agent 的对话历史LOG_CLASSIFIER_CHATTrueFalse是否打印分类器看到的对话历史LOG_CLASSIFIER_RAW_OUTPUTTrueFalse是否打印分类器的原始输出LOG_CLASSIFIER_OUTPUTTrueFalse是否打印分类意图结果命中 Agent、置信度LOG_EXECUTION_TIMESTrueFalse是否统计并打印各阶段执行耗时MAX_RETRIES33分类/调用的最大重试次数USE_DEFAULT_AGENT_IF_NONE_IDENTIFIEDFalseTrue分类器未选中任何 Agent 时是否回退到默认 Agent。示例置为False即未命中时直接返回无法处理提示CLASSIFICATION_ERROR_MESSAGE未设置None分类出错时的提示文本NO_SELECTED_AGENT_MESSAGE未设置内置兜底文案未选中 Agent 时返回给用户的提示GENERAL_ROUTING_ERROR_MSG_MESSAGE未设置None路由过程异常时的提示文本MAX_MESSAGE_PAIRS_PER_AGENT10100每个 Agent 保留的最大消息对数超出后裁剪历史从 orchestrator.py 的源码可以看出options也可以直接传字典框架会按OrchestratorConfig的字段自动过滤无效键。若不传classifier框架在检测到bedrock相关依赖可用时会默认创建BedrockClassifier否则抛出异常要求显式提供分类器。注册 Agentorchestrator.add_agent(create_tech_agent()) orchestrator.add_agent(create_travel_agent()) orchestrator.add_agent(create_health_agent())add_agent会把 Agent 加入内部字典self.agents并同步调用classifier.set_agents(self.agents)——这一步把每个 Agent 的id与description注入分类器的提示词模板是智能路由的数据来源对应 classifier.py 中的set_agents方法。Chainlit 事件绑定cl.on_chat_start async def start(): cl.user_session.set(user_id, str(uuid.uuid4())) cl.user_session.set(session_id, str(uuid.uuid4())) cl.user_session.set(chat_history, []) cl.on_message async def main(message: cl.Message): ...on_chat_start每次新建会话时生成唯一的user_id与session_id它们会作为编排器路由与历史存储的维度见后文on_message收到用户消息后先发送一个空消息占位msg.send()随后调用编排器核心方法response: AgentResponse await orchestrator.route_request(message.content, user_id, session_id, {})route_request是编排框架的总入口。对照 orchestrator.py 的源码其内部调用链为classify_request(...)拉取该会话的全部 Agent聊天历史调用分类器判定意图若无选中 Agent按USE_DEFAULT_AGENT_IF_NONE_IDENTIFIED决定是否回退默认 Agent否则返回内置的无法处理消息agent_process_request(...)通过dispatch_to_agent把请求交给选中 Agent 处理并先后把用户消息、Agent 回复写入会话存储finally中按LOG_EXECUTION_TIMES打印各阶段耗时。流式响应输出if isinstance(response, AgentResponse) and response.streaming is False: if isinstance(response.output, str): await msg.stream_token(response.output) elif isinstance(response.output, ConversationMessage): await msg.stream_token(response.output.content[0].get(text)) await msg.update()AgentResponse的streaming字段来自selected_agent.is_streaming_enabled()对应 agent.py 中AgentResponse数据类与基类的is_streaming_enabled。由于示例中三个 Agent 均开启了流式真正的 token 级流式输出由各 Agent 的回调完成这里只需在结束时update()刷新消息。三 Agent 定义与流式回调agents.pyexamples/chat-chainlit-app/agents.py 定义了三个工厂函数并实现了一个把 token 实时转发到 Chainlit 界面的回调类class ChainlitAgentCallbacks(AgentCallbacks): def on_llm_new_token(self, token: str) - None: asyncio.run(cl.user_session.get(current_msg).stream_token(token))这是示例中最值得借鉴的集成技巧AgentCallbacks是框架定义的钩子接口见 agent.pyLLM 每次吐出新 token 时都会被调用。回调里从 Chainlit 的user_session取出当前消息对象用asyncio.run把 token 流式推送到前端——因为回调是同步的而stream_token是异步方法所以需要asyncio.run桥接。注意app.py在on_message中提前执行了cl.user_session.set(current_msg, msg)回调才能取到它。三个 Agent 的定义要点def create_tech_agent(): return BedrockLLMAgent(BedrockLLMAgentOptions( nameTech Agent, streamingTrue, descriptionSpecializes in technology areas including software development, hardware, AI, ..., model_idanthropic.claude-3-sonnet-20240229-v1:0, callbacksChainlitAgentCallbacks() ))Tech Agent / Travel Agent均使用BedrockLLMAgent模型为 Claude 3 SonnetstreamingTrue开启流式Health Agent使用自定义的OllamaAgent模型为llama3.1:latest同样开启流式description是关键这段文本既是 Agent 的系统提示词素材也是分类器选择 Agent 的依据。从 bedrock_llm_agent.py 源码可以看到BedrockLLMAgent会把name与description拼进默认提示词模板You are a {name}. {description}...作为系统提示词同时分类器模板中的AGENT_DESCRIPTIONS也由各 Agent 的id: description拼接而成。描述写得越精准、边界越清晰路由准确率越高。本地 Agent 实现ollamaAgent.py健康 Agent 使用的是示例自定义的 examples/chat-chainlit-app/ollamaAgent.py它继承框架基类Agent并包装 Ollama Python 客户端dataclass class OllamaAgentOptions(AgentOptions): streaming: bool True model_id: str llama3.1:latest class OllamaAgent(Agent): async def process_request(self, input_text, user_id, session_id, chat_history, additional_paramsNone): messages [ {role: msg.role, content: msg.content[0][text]} for msg in chat_history ] messages.append({role: ParticipantRole.USER.value, content: input_text}) if self.streaming: return await self.handle_streaming_response(messages) else: response ollama.chat(modelself.model_id, messagesmessages) return ConversationMessage(roleParticipantRole.ASSISTANT.value, content[{text: response[message][content]}])要点解读历史消息重组框架传入的chat_history是ConversationMessage列表需要先转成 Ollama 所需的[{role: ..., content: ...}]字典列表再追加当前用户输入流式分支handle_streaming_response中以streamTrue调用ollama.chat逐段累加内容并同步触发self.callbacks.on_llm_new_token(...)——这正是 Chainlit 界面逐字输出的来源非流式分支直接取response[message][content]包装成ConversationMessage。这个文件的价值在于演示了如何为框架扩展一个全新的本地模型 Agent只要继承Agent、实现process_request并在流式路径中正确触发回调即可无缝接入编排器与 Bedrock Agent 平级共存。分类器的工作原理从 AgentMatcher 到工具调用为什么同一个route_request能自动把花粉过敏分给健康 Agent、把科技公司分给技术 Agent答案藏在分类器的提示词与工具调用机制中。AgentMatcher 系统提示词classifier.py 内置了一套名为AgentMatcher的系统提示词模板核心逻辑包括根据用户输入从agents列表中挑选最合适的 Agent 类型特殊处理追问模板明确要求如果用户输入是上一轮对话的延续如 yes、ok、I want to know more、数字答案等则沿用上一轮选中的 Agent——这正是 README 中第 4 个测试问题追问旅行 Agent能保持在同一 Agent 的原因对每个候选输出userinput、selected_agent、confidence字段模板中{{AGENT_DESCRIPTIONS}}与{{HISTORY}}两个占位符分别由set_agents注入的 Agent 描述列表和set_history格式化的历史消息填充对应update_system_prompt方法。结构化的工具调用bedrock_classifier.py 在调用模型时定义了一个名为analyzePrompt的 Bedrock tool输入 Schema 包含userinput、selected_agent、confidence三个字段。对 Anthropic 系模型还会设置toolChoice强制模型调用该工具拿到toolUse后通过get_agent_by_id(tool_use[input][selected_agent])映射回具体 Agent并携带置信度构造ClassifierResult。也就是说分类结果不是自由文本而是经过工具调用产出的结构化数据这让路由结果稳定、可解析。路由兜底在 orchestrator.py 的classify_request中若selected_agent为空且配置了USE_DEFAULT_AGENT_IF_NONE_IDENTIFIEDTrue会通过get_fallback_result()回退到default_agent。示例中该选项为False因此无法识别的问题会得到无法确定如何处理的礼貌回复。会话历史与多轮追问存储层如何工作示例没有显式传入storage因此框架默认使用InMemoryChatStorage内存存储见 in_memory_chat_storage.py。它按user_id#session_id#agent_id作为 key 组织对话特点包括按 Agent 隔离历史每个 Agent 只看到自己参与过的对话fetch_chat按 Agent 维度取历史而分类器通过fetch_all_chats看到该会话下所有 Agent 的完整历史并给助手消息加上[agent_id]前缀以便判断当前对话上下文去重与裁剪连续相同角色如两条相邻 user 消息不重复保存保存时按MAX_MESSAGE_PAIRS_PER_AGENT裁剪历史示例配置为 10 对时间戳排序消息带毫秒时间戳fetch_all_chats会按时间排序保证多 Agent 交错对话的顺序正确。这也解释了 README 中追问旅行 Agent测试问题的实现基础你的追问会与之前旅行 Agent 的上下文一起进入分类器分类器据历史沿用原 Agent再把完整历史交给该 Agent 生成连贯的续答。如果你需要持久化如跨会话保留历史框架还提供了 DynamoDB 存储 与 SQL 存储 等实现可参考 storage 概览文档。完整运行验证与预期效果按前面步骤启动后可用 README 给出的四类问题依次验证输入What are some best places to visit in Seattle?观察终端日志中Classified Intent显示命中travel-agent浏览器收到 Claude 3 Sonnet 生成的旅行建议流式输出输入What are some cool tech companies in Seattle应命中tech-agent输入What kind of pollen is causing allergies in Seattle?应命中health-agent由本地 Ollama 的 Llama 3.1 流式回答——这也是验证 Ollama 服务与模型是否就绪的最快方式针对第 1 问继续追问如Tell me more about the food there应保持路由到travel-agent并参考上一轮上下文作答。调试时善用app.py中开启的日志开关LOG_CLASSIFIER_OUTPUTTrue会打印命中的 Agent 与置信度LOG_EXECUTION_TIMESTrue会打印意图分类与Agent 处理两个阶段的耗时方便快速定位路由不准或响应缓慢的问题。常见问题与排查建议找不到multi_agent_orchestrator模块确认requirements.txt已安装或检查 Python 解释器是否为当前虚拟环境分类器报 AWS 相关错误确认 AWS 凭证已配置且 Bedrock 模型可用可先单独用boto3调用一次converse验证权限健康 Agent 无响应或报连接错误确认 Ollama 服务已启动ollama serve且已拉取llama3.1:latest路由结果不稳定尝试调低分类器的temperature分类任务推荐接近 0或细化各 Agent 的description边界追问没有保持同一 Agent检查MAX_MESSAGE_PAIRS_PER_AGENT是否过小导致历史被裁剪历史不足时分类器难以判断上下文。至此一个Bedrock 云上 Agent Ollama 本地 Agent Claude 智能路由 Chainlit 流式界面的完整多智能体应用就搭建完成了。你可以在此基础上继续探索本仓库的其他 Agent 类型如 Amazon Bedrock Agent、Bedrock Flows以及 编排器更多用法把它扩展成适合自己业务场景的多 Agent 助手。【免费下载链接】agent-squadFlexible and powerful framework for managing multiple AI agents and handling complex conversations项目地址: https://gitcode.com/GitHub_Trending/mu/agent-squad创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考