尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于nanobot与通义千问构建钉钉AI助手:从零实现智能派活机器人

基于nanobot与通义千问构建钉钉AI助手:从零实现智能派活机器人 1. 项目缘起当“派活”遇上AI一个真实的生产力痛点在团队协作里最让人头疼的往往不是技术难题本身而是那些琐碎、重复但又必须有人去处理的“小活”。比如每天要手动汇总几个渠道的数据生成日报或者根据客户反馈的关键词去内部知识库搜一圈资料再或者新同事入职时需要有人一步步告诉他项目环境怎么搭、代码怎么拉。这些任务本身不复杂但极其消耗人的注意力和时间打断深度工作流。过去我们可能会写个脚本或者依赖某个固定的流程文档但脚本需要触发文档需要人去看都不够“丝滑”。直到AI Agent的概念火起来尤其是像nanobot这样轻量、可编程的框架出现事情开始变得有趣。我们能不能做一个“智能小工”把它放到钉钉这种大家每天必用的协作工具里让它7x24小时待命用自然语言就能给它派活它还能调用各种能力把活干完甚至把结果主动推回来这个想法就是我们这个“国产小龙虾方案”的起点——像吃小龙虾一样把那些琐碎的“壳”重复劳动剥掉只享受核心的“肉”价值创造。核心三件套nanobot作为机器人的“大脑”和“手脚”通义千问Qwen提供强大的自然语言理解与生成能力作为“思维”钉钉机器人则是它融入我们工作流的“身体”和“接口”。2. 方案核心组件选型与架构解析为什么是这三个组合这背后是一套经过权衡的“性价比”架构。2.1 nanobot轻量级AI Agent的“骨架”nanobot不是一个大众熟知的名字但在特定的开发者圈子里它正成为快速构建AI Agent的热门选择。你可以把它理解为一个高度模块化、事件驱动的机器人框架。它核心解决了两个问题“如何响应”和“如何执行”。事件驱动模型nanobot的核心是监听各种事件比如钉钉的消息、HTTP接口调用、定时任务触发然后根据预定义的逻辑进行处理。这非常适合IM机器人场景你的消息就是触发它的事件。技能Skill插件化它的能力不是固化的而是通过加载不同的“技能”插件来扩展。一个技能就是一个独立的Python模块负责处理一类具体的任务比如“查询天气”、“执行Shell命令”、“调用某个API”。这种设计让功能的增删改变得非常清晰和简单。与LLM的松耦合nanobot本身不绑定任何特定的大语言模型LLM。它通过一个标准的接口与LLM对话你可以在配置文件中轻松切换不同的LLM服务提供商比如今天用通义千问明天想试试GPT改个配置就行代码几乎不用动。选型理由相比于一些重型的、面向复杂规划的Agent框架如LangChain、AutoGennanobot更轻、更直接学习曲线平缓对于“接收指令-执行任务-返回结果”这类明确的工作流它的开销和复杂度都更低更像一个“机器人操作系统”。2.2 通义千问Qwen本土化强大的“思维引擎”通义千问是阿里云推出的开源大语言模型系列。选择它而非常见的OpenAI接口主要基于以下几点考虑合规与可控性所有数据在国内处理无需担忧跨境数据传输的政策与延迟风险这对于企业级应用是首要考量。成本与性能Qwen系列提供了从7B到72B不同规模的模型并且有专门优化的API服务。对于机器人场景通常不需要动用千亿参数模型一个效果不错的7B或14B模型通过其API调用在响应速度和成本间能取得很好的平衡。实测下来Qwen在中文指令理解、上下文关联和代码生成方面表现相当可靠。生态集成作为阿里系产品与钉钉、阿里云函数计算等服务的集成理论上会更顺畅文档和案例也更丰富。在我们的方案里Qwen扮演着“意图理解”和“内容生成”的核心角色。nanobot将用户模糊的自然语言指令如“帮我查一下上周项目A的日志错误”传递给QwenQwen需要解析出用户的真实意图“查询日志”并提取关键参数“项目A”、“上周”、“错误”。有时它还需要根据对话历史进行多轮澄清。2.3 钉钉机器人无缝嵌入工作流的“交互界面”钉钉机器人是钉钉开放平台提供的能力它允许开发者创建一个自定义的机器人并将其添加到群聊或单聊中。它成为了我们AI Agent最自然的入口。开箱即用的通道无需自己搭建WebSocket或长轮询钉钉负责消息的接收和推送我们只需要提供一个HTTPS的回调地址Webhook供钉钉在收到消息时调用。丰富的消息格式支持文本、链接、Markdown、ActionCard交互卡片等多种消息格式这让我们的机器人回复可以非常美观和交互性强。例如返回一个数据汇总时可以用Markdown表格提供一个操作选择时可以用带按钮的卡片。安全的鉴权机制支持签名验证确保发送到我们服务端的请求确实来自钉钉防止恶意调用。架构全景图用户机器人并发送指令 - 钉钉服务器将消息封装成HTTP POST请求发送到我们部署的nanobot服务 - nanobot接收到事件触发对应的消息处理流程 - nanobot调用配置的Qwen API进行意图识别和参数提取 - 根据识别结果nanobot调用对应的技能Skill来执行具体任务如查询数据库、调用外部API、执行脚本- 技能执行完毕将结果返回给nanobot - nanobot将结果格式化为钉钉消息通过钉钉提供的API发送回对应的群聊或会话。整个流程形成了一个闭环。3. 从零到一环境搭建与核心配置实战理论讲完我们动手搭一个。假设你已经有一个Linux服务器或本地开发环境并且拥有钉钉开发者账号和阿里云API密钥。3.1 基础环境准备首先为我们的“小龙虾”准备一个干净的“厨房”。# 1. 创建并进入项目目录 mkdir nanobot_dingtalk_agent cd nanobot_dingtalk_agent # 2. 创建虚拟环境强烈推荐避免依赖冲突 python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 3. 安装nanobot核心包 pip install nanobot-core # 根据你需要安装额外的社区技能包例如一个用于HTTP请求的通用技能 pip install nanobot-skill-http # 4. 安装通义千问的SDK # 这里假设使用DashScope阿里云灵积的API它是调用通义千问的官方途径 pip install dashscope3.2 钉钉机器人创建与配置这是连接现实世界的第一步。登录钉钉开发者后台找到你的组织进入“应用开发” - “企业内部开发” - “机器人”。创建机器人点击“创建应用”选择“机器人”填写名称和描述。创建成功后记录下AppKey和AppSecret这是机器人身份的凭证。配置消息接收在机器人详情页找到“消息接收”配置。这里需要填写一个公网可访问的URL即我们即将部署的nanobot服务的Webhook地址。例如https://your-server.com/dingtalk/callback。在开发阶段你可以使用内网穿透工具如ngrok、frp将本地服务暴露为一个临时公网地址进行测试。获取Webhook地址在“机器人详情”页你还能找到一个“Webhook”地址格式如https://oapi.dingtalk.com/robot/send?access_tokenXXX。这个地址用于主动发送消息到钉钉。我们nanobot在完成任务后会调用这个地址把结果推回去。请妥善保管这个access_token。发布与安装将机器人发布到你的组织并把它添加到需要使用的钉钉群中。3.3 nanobot项目初始化与核心逻辑编写现在我们来编写机器人的“大脑”和“反射弧”。# 在项目根目录初始化一个nanobot项目结构 nanobot init my_dingtalk_bot cd my_dingtalk_bot你会看到一个基础的项目结构包含configs,skills,main.py等。我们主要关注三个文件1. 配置文件 (configs/config.yaml): 机器人的“基因”name: 小龙虾助手 description: 一个帮你处理琐事的AI小工 # LLM配置连接通义千问 llm: provider: dashscope # 指定提供商 model: qwen-plus # 使用qwen-plus模型可根据需要换为 qwen-turbo, qwen-max等 api_key: ${DASHSCOPE_API_KEY} # 从环境变量读取API Key更安全 parameters: temperature: 0.1 # 降低随机性让回答更稳定 top_p: 0.8 # 技能配置加载我们自定义的技能 skills: - skills.my_custom_skill # 一个自定义的示例技能 - nanobot_skill_http # 引入的第三方HTTP技能 # 事件监听器配置处理钉钉消息 listeners: - name: dingtalk_webhook type: webhook # 监听Webhook请求 path: /dingtalk/callback # 对应钉钉后台配置的路径 method: POST2. 自定义技能 (skills/my_custom_skill.py): 机器人的“手艺”技能是能力的载体。这里我们创建一个简单的技能它能够理解用户关于“时间”的询问。import logging from datetime import datetime from nanobot.skills import Skill, skill from nanobot.messages import TextMessage logger logging.getLogger(__name__) skill(nametime_query, description查询当前时间或日期) class TimeQuerySkill(Skill): 一个简单的示例技能当用户询问时间时返回当前时间。 async def execute(self, context, **kwargs): 技能执行入口。 context: 包含当前会话、用户消息等上下文信息。 user_message context.current_message.text # 这里可以做得更智能比如用LLM判断意图。 # 但作为示例我们简单匹配关键词。 if 时间 in user_message or 几点 in user_message: now datetime.now().strftime(%Y-%m-%d %H:%M:%S) reply_text f现在是北京时间{now} # 返回一个文本消息对象nanobot会将其转换为钉钉消息格式 return TextMessage(contentreply_text) # 如果不匹配返回Nonenanobot会尝试其他技能或使用LLM直接回复 return None3. 主程序适配钉钉 (main.py): 消息的“翻译官”nanobot默认的Webhook监听器可能不直接兼容钉钉的消息格式。钉钉发送过来的是一套特定的JSON结构。我们需要一个适配器来“翻译”。import hashlib import hmac import base64 import time import json from urllib.parse import quote_plus from nanobot import Nanobot from nanobot.listeners import WebhookListener from fastapi import FastAPI, Request, Header, HTTPException from fastapi.responses import JSONResponse # 从环境变量或配置中读取钉钉的签名密钥 import os DINGTALK_SECRET os.getenv(DINGTALK_SECRET, ) app FastAPI() bot Nanobot.from_config() # 自定义一个钉钉消息解析函数 def parse_dingtalk_message(request_data: dict): 将钉钉的Webhook JSON格式转换为nanobot能理解的内部消息格式。 msg_type request_data.get(msgtype, text) if msg_type text: content request_data.get(text, {}).get(content, ).strip() # 移除可能存在的机器人名字 # 钉钉消息格式可能是“小龙虾助手 现在几点” # 这里简单处理实际可能需要更精确的解析 if content.startswith(): # 简单移除第一个和其后的空格或直到下一个空格 parts content.split( , 1) if len(parts) 1: content parts[1] else: content sender_id request_data.get(senderStaffId, ) # 发送者ID conversation_id request_data.get(conversationId, ) # 会话ID # 构造nanobot需要的消息字典 nanobot_msg { text: content, user_id: sender_id, conversation_id: conversation_id, platform: dingtalk, raw_data: request_data # 保留原始数据以备后用 } return nanobot_msg # 可以继续处理其他消息类型如图片、链接等 return None # 钉钉签名验证函数重要用于安全验证 def verify_dingtalk_signature(timestamp: str, sign: str, secret: str, body: str): 验证钉钉回调请求的签名。 参考钉钉官方文档https://open.dingtalk.com/document/robots/customize-robot-security-settings if not secret: return True # 如果未设置SECRET跳过验证不推荐生产环境 string_to_sign f{timestamp}\n{secret} hmac_code hmac.new(secret.encode(utf-8), string_to_sign.encode(utf-8), digestmodhashlib.sha256).digest() my_sign base64.b64encode(hmac_code).decode(utf-8) return hmac.compare_digest(my_sign, sign) app.post(/dingtalk/callback) async def dingtalk_callback(request: Request, timestamp: str Header(None, aliastimestamp), sign: str Header(None, aliassign)): 钉钉Webhook回调入口。 try: body_bytes await request.body() body_str body_bytes.decode(utf-8) request_data json.loads(body_str) # 1. 签名验证 if not verify_dingtalk_signature(timestamp, sign, DINGTALK_SECRET, body_str): raise HTTPException(status_code403, detailInvalid signature) # 2. 解析钉钉消息为nanobot格式 nanobot_msg parse_dingtalk_message(request_data) if not nanobot_msg: return JSONResponse(content{msg: Unsupported message type}) # 3. 将消息交给nanobot核心处理 # 这里我们同步调用对于简单场景够用。复杂场景可考虑异步队列。 responses await bot.process_message(nanobot_msg) # 4. 将nanobot的回复消息通过钉钉的Webhook发送回去 # 注意这里需要你机器人的access_token dingtalk_webhook_url fhttps://oapi.dingtalk.com/robot/send?access_tokenYOUR_ACCESS_TOKEN for resp in responses: if hasattr(resp, to_dingtalk): # 如果消息对象有转换为钉钉格式的方法 dingtalk_msg resp.to_dingtalk() else: # 默认处理文本消息 dingtalk_msg { msgtype: text, text: {content: resp.content} } # 使用requests或aiohttp发送消息到钉钉此处省略发送代码 # await send_to_dingtalk(dingtalk_webhook_url, dingtalk_msg) print(f[待发送] 钉钉消息: {dingtalk_msg}) # 开发阶段先打印 return JSONResponse(content{msg: ok}) except json.JSONDecodeError: raise HTTPException(status_code400, detailInvalid JSON) except Exception as e: logging.error(f处理钉钉回调失败: {e}) raise HTTPException(status_code500, detailInternal server error) if __name__ __main__: import uvicorn # 启动FastAPI服务监听在8080端口 uvicorn.run(app, host0.0.0.0, port8080)这个main.py做了几件关键事验证钉钉请求的真实性、将钉钉格式的消息“翻译”成nanobot能懂的结构、将nanobot处理后的结果再“翻译”回钉钉格式并发送。你需要将YOUR_ACCESS_TOKEN替换为你的机器人Webhook地址中的token并实现send_to_dingtalk函数可以使用aiohttp库。3.4 连接通义千问让机器人“听懂人话”上面的自定义技能只是简单匹配关键词。要真正理解复杂、模糊的指令必须引入Qwen。我们需要修改技能让它学会“问”Qwen。首先确保在config.yaml中正确配置了llm部分并且环境变量DASHSCOPE_API_KEY已设置。然后我们创建一个更高级的技能例如一个“智能问答”技能它会把所有无法被其他技能处理的指令都交给Qwen来处理并可以联网搜索。# skills/smart_qa_skill.py import logging from nanobot.skills import Skill, skill from nanobot.messages import TextMessage from nanobot.llm import LLMClient # 引入LLM客户端 logger logging.getLogger(__name__) skill(namesmart_qa, description通用智能问答与指令理解, priority50) # priority较低让具体技能先匹配 class SmartQASkill(Skill): 一个利用Qwen进行通用问答和意图理解的技能。 它可以作为兜底技能处理其他技能无法处理的模糊指令。 def __init__(self, config): super().__init__(config) # 从nanobot的配置中获取LLM客户端实例 self.llm_client LLMClient.from_config(config) async def execute(self, context, **kwargs): user_message context.current_message.text if not user_message: return None # 构建一个系统提示词引导Qwen更好地理解机器人场景 system_prompt 你是一个部署在钉钉上的AI助手名叫“小龙虾助手”。你的主要职责是帮助用户处理工作琐事。 用户可能会给你一些模糊的指令你需要尝试理解其背后的意图。 常见的意图包括但不限于查询信息天气、时间、股票、公司内部数据、执行简单计算、翻译、总结文本、生成代码片段、回答问题等。 如果你判断用户的指令需要调用某个具体工具或技能比如查天气需要调用API你可以回复一个结构化的指令例如 [ACTION:weather_query location北京]。 否则请直接给出友好、有帮助的文本回答。 请用中文回复。 # 准备对话历史如果有的话 messages [ {role: system, content: system_prompt}, {role: user, content: user_message} ] try: # 调用通义千问API response await self.llm_client.chat_completion( messagesmessages, temperature0.2, # 较低的随机性保证回答稳定 max_tokens1024 ) ai_reply response.choices[0].message.content.strip() # 这里可以添加后处理解析AI回复中是否包含结构化指令 [ACTION:...] # 如果包含可以触发另一个对应的技能如weather_query # 这是一个简单的实现示例 if ai_reply.startswith([ACTION:) and ai_reply.endswith(]): # 解析动作和参数这里简化处理 action_str ai_reply[8:-1] # 去掉[ACTION:和] # 理论上这里应该调度到其他技能本例中我们只做演示 logger.info(f识别到结构化指令: {action_str}) return TextMessage(contentf“我已理解您想执行 {action_str}相关功能正在开发中。”) else: # 直接返回AI的文本回答 return TextMessage(contentai_reply) except Exception as e: logger.error(f调用Qwen API失败: {e}) return TextMessage(content抱歉我的大脑Qwen服务暂时开小差了请稍后再试。)别忘了在config.yaml的skills列表里加上这个新技能- “skills.smart_qa_skill”。现在你的机器人就接上了通义千问的“大脑”能够理解更复杂的指令并进行智能对话了。4. 部署、调试与进阶实战技巧让代码跑起来只是第一步让它稳定、好用才是关键。4.1 本地调试与内网穿透在开发阶段你的服务运行在本地localhost钉钉的服务器无法直接访问。你需要一个“桥梁”。使用 ngrok (最简便)# 下载ngrok并注册获取authtoken ngrok config add-authtoken YOUR_AUTH_TOKEN # 将本地的8080端口暴露到公网 ngrok http 8080运行后ngrok会给你一个随机的https://xxx.ngrok.io地址。将这个地址后面加上/dingtalk/callback填到钉钉机器人的“消息接收”URL中。使用 frp (更稳定可控)如果你有自己的云服务器可以搭建frp服务获得一个固定的二级域名更适合长期测试。调试技巧在main.py中多使用logging打印关键信息如接收到的原始数据、解析后的消息、调用Qwen的请求和响应。钉钉机器人平台也有“消息推送记录”可以查看发送是否成功。4.2 服务器部署与进程守护开发完成后需要部署到正式的服务器。使用 systemd (Linux)创建一个服务文件/etc/systemd/system/nanobot-dingtalk.service。[Unit] DescriptionNanobot DingTalk AI Agent Afternetwork.target [Service] Typesimple Userwww-data # 根据你的情况修改 WorkingDirectory/path/to/your/nanobot_dingtalk_agent EnvironmentPATH/path/to/your/venv/bin EnvironmentDASHSCOPE_API_KEYyour_key_here EnvironmentDINGTALK_SECRETyour_secret_here ExecStart/path/to/your/venv/bin/python main.py Restartalways RestartSec10 [Install] WantedBymulti-user.target然后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable nanobot-dingtalk sudo systemctl start nanobot-dingtalk sudo systemctl status nanobot-dingtalk # 查看状态使用 Docker (推荐便于隔离和迁移)编写Dockerfile和docker-compose.yml将应用、Python环境、配置文件打包。这样可以在任何有Docker的环境一键启动。4.3 设计更强大的技能以“数据查询”为例一个真正的“派活”机器人必须能操作真实的数据或系统。我们设计一个技能让机器人能查询数据库以MySQL为例并返回结果。# skills/data_query_skill.py import aiomysql import logging from nanobot.skills import Skill, skill from nanobot.messages import TextMessage, MarkdownMessage logger logging.getLogger(__name__) skill(namedata_query, description查询指定数据库表的数据, patterns[“查一下”, “数据”, “报表”]) # 可以加一些触发关键词 class DataQuerySkill(Skill): def __init__(self, config): super().__init__(config) self.db_config { host: config.get(db_host, localhost), port: config.get(db_port, 3306), user: config.get(db_user, root), password: config.get(db_password, ), db: config.get(db_name, test), charset: utf8mb4 } self.pool None async def connect_db(self): 创建数据库连接池 if self.pool is None: self.pool await aiomysql.create_pool(**self.db_config) return self.pool async def execute(self, context, **kwargs): user_message context.current_message.text # 这里应该集成LLM进行意图识别和SQL生成。 # 为了安全我们不做全自然语言转SQL而是预定义几个查询模板。 # 例如用户说“查一下上周的订单总数” # 我们可以用LLM提取出“上周”和“订单总数”然后映射到预定义的SQL模板。 # 假设我们通过LLM或简单规则已经确定了查询参数 query_type “last_week_order_count” # 这个应由更高级的意图识别模块提供 params {} if query_type “last_week_order_count”: sql “SELECT COUNT(*) as total_orders FROM orders WHERE order_date DATE_SUB(CURDATE(), INTERVAL 7 DAY)” result_key “total_orders” result_desc “上周订单总数” else: return None # 不处理其他类型 try: pool await self.connect_db() async with pool.acquire() as conn: async with conn.cursor(aiomysql.DictCursor) as cur: await cur.execute(sql, params) result await cur.fetchone() if result: # 将结果格式化为Markdown表格或文本 reply_md f“**{result_desc}**\n\n” reply_md f“{result_key}: **{result[result_key]}**\n” # 可以构造更复杂的表格 # reply_md “| 指标 | 值 |\n| :--- | :--- |\n” # for k, v in result.items(): # reply_md f“| {k} | {v} |\n” return MarkdownMessage(contentreply_md) else: return TextMessage(content“未查询到相关数据。”) except Exception as e: logger.exception(f“数据库查询失败: {e}”) return TextMessage(content“查询数据时出现错误请检查数据库连接或查询语句。”) async def cleanup(self): 技能卸载时关闭连接池 if self.pool: self.pool.close() await self.pool.wait_closed()这个技能展示了如何连接外部资源。关键点在于永远不要将用户输入直接拼接成SQLSQL注入风险应该通过LLM或固定模板将自然语言转换为安全的查询参数。4.4 安全与权限管控把机器人放进工作群安全是重中之重。请求签名验证如前文代码所示必须验证钉钉回调的签名(timestampsign)这是防止伪造请求的第一道防线。技能执行权限不是所有用户都能调用所有技能。可以在Skill的execute方法开头检查context.current_message.user_id对照一个权限列表可以配置在文件或数据库里。例如只有管理员才能执行“重启服务”这样的高危技能。敏感信息脱敏从数据库或API查询到的结果在发送到钉钉前要过滤掉手机号、身份证号、密钥等敏感信息。限流与防刷对用户的请求频率做限制防止恶意调用消耗你的LLM API额度或数据库资源。可以在main.py的入口处添加简单的计数器或使用Redis实现更复杂的限流。网络隔离部署机器人的服务器应处于内网或具有严格安全组策略的网络中仅开放必要的端口如80/443给钉钉回调。4.5 监控与日志一个健壮的服务离不开监控。结构化日志使用Python的logging模块配置JSON格式输出方便接入ELKElasticsearch, Logstash, Kibana或类似日志系统。记录每一次请求、LLM调用、技能执行结果和耗时。健康检查为你的FastAPI服务添加一个/health端点返回服务状态、数据库连接状态等。方便运维监控。关键指标统计技能调用次数、Qwen API调用耗时与Token消耗、错误率等。这些数据可以帮助你优化技能设计和成本控制。5. 避坑指南与效能提升心法在实际开发和运维中我踩过不少坑也总结了一些让“小龙虾”更好用的经验。5.1 意图识别的“最后一公里”难题最初我试图让Qwen直接解析所有指令并返回可执行的命令。但发现对于边界模糊的指令效果不稳定。比如“看看张三上周的业绩”Qwen可能理解成“查询数据库”也可能直接生成一段文本描述。我的解决方案是分层处理第一层精确技能匹配。先定义一批高频、明确的技能并用关键词或正则表达式触发。如“/天气 北京”、“打卡”。第二层LLM意图分类。对于未匹配的指令交给Qwen但任务不是直接执行而是分类。我训练或通过提示词引导Qwen将指令分类到预定义的几个“技能槽”如[QUERY_DATA]、[CALCULATE]、[GENERATE_TEXT]等并提取结构化参数。第三层技能执行与LLM润色。根据分类结果调用对应的技能获取原始数据或结果再将这个结果和原始指令一起交给Qwen让它生成一段通顺、友好的最终回复。这样既保证了关键动作的准确执行又利用了LLM的泛化理解和语言生成能力。5.2 钉钉消息格式的“坑”钉钉的消息格式比较独特稍不注意就会发送失败或显示异常。Markdown表格对齐钉钉的Markdown对表格支持有限复杂的表格可能渲染错乱。建议简单表格用|复杂数据可以考虑用“字段值”的列表形式或者直接生成图片通过技能调用图表生成API发送。特定用户在机器人回复中想某人需要在文本中使用用户手机号并且同时传递at字段。但获取群成员的手机号需要额外的钉钉API权限需申请。通常我们只在回复中触发指令的用户context.current_message.sender_id对应的手机号。消息长度限制钉钉单条文本消息有长度限制约5000字符。如果回复内容很长需要主动切割成多条发送或者先生成一个文件如txt然后发送文件消息。ActionCard按钮回调如果你使用了交互卡片按钮点击后的回调地址也必须是公网可访问的且同样需要处理签名验证。这相当于为你的机器人增加了“事件”处理能力可以实现更复杂的交互流程。5.3 成本控制与性能优化Qwen API是按Token收费的无节制地使用会让账单飞涨。设计精简的提示词Prompt系统提示词不要过于冗长。在满足指令清晰的前提下尽量简短。将固定的上下文如公司背景、常用指令说明做得精炼。缓存LLM响应对于一些常见、结果相对固定的查询如“公司简介”、“产品列表”可以将Qwen的回复缓存起来用Redis或内存缓存设定一个合理的过期时间。下次遇到相同或相似问题时直接返回缓存结果大幅节省Token和延迟。设置使用限额为每个用户或每个群设置每日/每周的LLM调用次数上限。可以在技能执行前检查计数。异步与非阻塞在main.py中处理钉钉回调并调用LLM和技能的过程如果耗时较长5秒钉钉可能会超时重试。建议将核心处理逻辑放入异步任务队列如Celery Redis或直接用asyncio.create_task在收到回调后立即返回“ok”然后异步处理任务处理完再主动推送结果。这需要更复杂的架构但能提供更好的用户体验和可靠性。5.4 从“玩具”到“工具”的演进一个能回答问题的机器人和一个能真正“派活”的Agent差距在于状态管理和工作流。会话状态nanobot本身支持简单的会话上下文。但对于一个需要多步交互的复杂任务比如“帮我订会议室要下午2点10个人的”你需要自己维护一个更强大的状态机。可以将对话状态当前任务、已收集的参数、下一步动作存储到数据库或Redis中以conversation_iduser_id为键。工作流引擎对于极其复杂的任务可以考虑集成一个轻量级的工作流引擎如Prefect的核心逻辑或自己实现一个简单的状态机。将任务分解为多个步骤每个步骤可能调用一个技能或询问用户根据上一步的结果决定下一步的走向。这样你的机器人就能处理“需求收集-方案制定-执行-反馈”的完整闭环了。技能市场与动态加载当技能越来越多可以设计一个技能注册中心。甚至允许用户在钉钉上通过特定指令“安装”或“启用”某个技能实现功能的动态扩展。nanobot的插件化架构为这提供了可能。这个“国产小龙虾方案”从构思到上线最深的体会是技术选型的“轻”与“巧”比“大”与“全”更重要。nanobot的轻量化让我们能快速聚焦业务逻辑通义千问的本地化能力消除了合规顾虑钉钉则提供了现成的、高粘性的用户入口。三者结合确实像吃小龙虾一样用最小的代价剥壳获取了最大的满足感吃肉。它可能不是功能最强大的Agent但绝对是能最快在你团队里跑起来、并真切解决一些痛点的那一个。接下来我打算为它增加一个“技能学习”功能让团队成员能通过自然语言描述共同教会它处理新的琐事让这个“小工”越来越能干。
返回列表