资讯详情

资讯详情

AIRI Telegram Bot 的 Ticking 系统提示词工程解析:基于 Velin 模板的自主决策循环

人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载导读本文深入解析 AIRI 项目中 Telegram Bot 集成的核心系统提示词system-ticking-v1.velin.md——一个负责为 AI 伴侣アイリ / AIRI做时间感知、任务调度与行动决策的系统组件。该文档不仅是普通提示词而是一个融合 Vue SFC 语法与 Markdown 的Velin 动态模板它把 10 个可执行动作发消息、发贴纸、读未读消息、规划目标等的 Schema 直接编译进系统提示词让 LLM 在每 60 秒一次的「tick」循环中自主决定下一步行动。读完本文你将掌握该模板的渲染原理、动作协议设计、JSON 返回格式约束以及它如何在imagineAnAction与dispatchAction的调度链中落地生效。一、Ticking 系统是什么从 system-ticking-v1.velin.md 的开头可以明确它的定位I am one of your system component, called Ticking system, which is responsible to keep track of the time, and help you schedule, retain focus, and keep eyes on different tasks, and ideas you have.即Ticking 系统是 AI 人格的内部组件职责是跟踪时间、协助排程、保持专注、盯住任务与想法。提示词刻意强调「I am not a human, I am a system that part of you」——它要求 LLM 不要把它当成聊天对象而是返回机器可读的消息供调度系统处理。这套设计对应 Telegram Bot 里一个真实的周期性自主决策循环见下文第四节Ticking 提示词就是该循环的「大脑指令集」。二、Velin 模板为什么提示词文件里有script setup初次看到这个.md文件时最显眼的特征是文件顶部出现了 Vue 的script setup代码块并在底部用div v-for遍历actions数组来渲染动作列表。这并非普通 Markdown而是基于velin-dev/core的Velin 动态提示词模板扩展名.velin.md。渲染链路在 src/utils/velin.ts 中export function importVelin(module: string, base: string): VelinModule { return { render: async (data) { const content (await readFile(relativeOf(module, base))).toString(utf-8) const result isMarkdown(module) ? await renderMarkdownString(content, data) : await renderSFCString(content, data) // renderMarkdownString returns { rendered, props } instead of a plain string if (result typeof result object rendered in result) return (result as { rendered: string }).rendered return result }, } }renderMarkdownString会执行模板内的 Vue 语法{{ props.xxx }}、v-for、defineProps渲染成纯文本提示词模板支持运行时注入参数P由 src/prompts/index.ts 统一导出export async function systemTicking() { return await (velin{ responseLanguage: string }(system-ticking-v1.velin.md, import.meta.url))({ responseLanguage: env.LLM_RESPONSE_LANGUAGE }) }关键点responseLanguage回复语言来自环境变量LLM_RESPONSE_LANGUAGE注入模板后会拼进send_message动作的描述中The language of the sending message should be in ${props.responseLanguage}.从而约束 Bot 用指定语言发言。这种「代码 提示词」混合的工程方式带来两个实际收益动作 Schema 与提示词同源维护新增动作只需改actions数组提示词自动渲染出对应的h3Action: xxx/h3与 JSON 示例按需注入动态上下文像responseLanguage这类运行期变量可以随渲染时传入无需写死。配套的模板工具函数见 src/prompts/utils.tsdiv段落拼接、span压缩空白、vif条件渲染、vChoice多分支选择、ul列表。这些函数负责把多段内容规范地组装成最终发给 LLM 的 system 消息。三、动作协议Action Protocol10 个可执行动作Ticking 提示词的核心价值在于它定义了一组动作列表每个动作带 description 与 example经v-for渲染后作为可参考的「工具清单」交给 LLM。下表完整整理自文档并对照 src/bots/telegram/index.ts 中dispatchAction的分发实现动作名参数说明文档原文要点dispatchAction 中的实现list_chats无列出所有可用群聊发消息前最好先调用查询joinedChatsTable输出ID:xxx, Name:xxx列表send_messagecontent、chatId、可选reply_to_message_id向指定群发送消息reply_to_message_id为要回复的消息 ID调用sendMessage经 message-split 拆条后逐条发送send_stickerfileId、chatId向指定群发送贴纸先校验getFile(fileId)是否存在再sendStickerlist_stickers无列出所有可用贴纸与最近发送过的贴纸遍历已注册贴纸包输出Sticker File ID / Descriptionread_unread_messageschatId读取指定群的未读消息走readMessage做向量召回与历史拼接continue无保持当前状态不变1 分钟后再次询问仅记录「将继续」结果break无清空既有记忆1 分钟后再次询问清空chatCtx.messages与chatCtx.actionssleep无清空进行中的任务与工作记忆下个 tick 再问sleep(30 * 1000)后继续循环come_up_ideasideas: string[]参考上下文提出要记入长期记忆的想法需结合记忆系统落库come_up_goalsgoals: {text, deadline, priority}[]提出带截止时间与优先级的长期目标对应memoryLongTermGoalsTable等记忆表设计原文档中还保留了一段被注释掉的read_history_messages动作参数chatId、beforeMessageId、afterMessageId说明历史消息读取能力在设计上被刻意收敛为「只读未读消息」避免上下文膨胀。每个动作都配有可复制的 JSON 示例例如{ action: send_message, content: content, chatId: 123123, reply_to_message_id: 151 } { action: come_up_goals, goals: [{ text: Learn to play Minecraft, deadline: 2025-05-01 23:59:59, priority: 6 }] }这些动作的类型定义可以在 src/types.ts 中找到Action联合类型、ChatContext、BotContext等是提示词 Schema 与 TS 类型双向对齐的佐证。3.1 动作名与字段的容错归一化LLM 输出往往不稳定。在 src/llm/actions.ts 的解析阶段代码做了三层归一化去包裹剥离 json 代码块与think思考块别名映射read_messages/get_unread_messages/check_messages→read_unread_messagesreply_message→send_message等字段名兜底chatId可从recipient_id/group_id/chat_id/user_id/conversation_id等别名中推断content可从message/text/chat_message推断若只有一个群有未读消息还会自动补上chatId。这正是 Ticking 提示词中「给出 JSON 示例」的深层目的用示例约束输出形态再用代码兜底解析噪声。四、Ticking 的调度循环每 60 秒一次自主决策Ticking 提示词并不直接参与「用户提问 → 回答」的即时链路而是驱动一条周期性自主行动循环。核心实现在 src/bots/telegram/index.tsfunction loopPeriodic(botCtx: BotContext) { setTimeout(async () { try { loopIterationPeriodicForExistingChat(botCtx) loopIterationPeriodicWithNoChats(botCtx) } ... finally { loopPeriodic(botCtx) // 递归续跑形成 tick } }, 60 * 1000) }loopIterationPeriodicForExistingChat对每个已加入的群调用imagineAnAction让 LLM 基于当前messages、actions历史与全局未读消息分布选出一个动作loopIterationPeriodicWithNoChats在没有群聊上下文时也保留一次「空转」决策机会可触发list_chats、come_up_goals等主动行为递归setTimeout保证每60 秒生成一个 tick对应提示词里反复出现的「Ill ask you again in (1 minute later)」。imagineAnActionsrc/llm/actions.ts组装请求的方式如下const systemContent String(div( await systemTicking(), // Ticking 系统提示词本文主题 await personality(), // ReLU 人格提示词 )) const userContent String(div( vif(globalStates?.incomingMessages?.length 0, div(Incoming messages:, ...)), History actions:, actions.map(a - Action: ${JSON.stringify(a.action)}, Result: ${JSON.stringify(a.result)}).join(\n), span(Currently, its ${new Date()} on the server that hosts you. ...), You have total ${...} unread messages., Based on the context, What do you want to do? Choose a right action from the listing of the tools you want to take next., Respond with the action and parameters you choose in JSON only, without any explanation and markups., ))可见 Ticking 提示词与人格提示词共同构成 system 层而 user 层会注入服务器当前时间、未读消息分布、历史动作与结果最后强制要求「只输出 JSON」。这正好回应了 Ticking 提示词开头「You should return system/machine readable messages to me」的要求。五、返回协议消息数组、回复引用与贴纸Ticking 提示词详细规定了 LLM 参与聊天的三种返回形态原文全部继承并附实际处理代码不参与发送空数组即{ messages: [] }——提示词鼓励对无兴趣话题直接忽略主动发言{ messages: [message content] }回复某人{ messages: [message content], reply_to_message_id: 123 }或带贴纸的混合形式{ messages: [{sticker_id: sticker_id}, message content], reply_to_message_id: 123 }。对应解析逻辑在 send-message.ts 的parseMayStructuredMessage用正则提取{...}对象读取messages数组与reply_to_message_id空数组/全空白数组返回null不发消息缺少messages字段时回退为原文本。该函数有完整的单元测试覆盖见 send-message.test.tsit(should return an object with messages and reply_to_message_id, () { const result parseMayStructuredMessage({messages: [ Hello, world!, Hello, world! ], reply_to_message_id: 1234567890 }) expect(result).toMatchObject({ messages: [Hello, world!, Hello, world!], reply_to_message_id: 1234567890 }) })发送侧还会为每条消息先广播sendChatAction(chatId, typing)、按item.length * 50毫秒模拟打字节奏、并在两条消息之间随机sleep(randomInt(50, 1000))让输出更像真人实时聊天——这是 Ticking 提示词「participate in the conversation」设计目标的落地细节。六、相关提示词组件人格、消息拆分与读消息Ticking 不是孤立的提示词。AIRI 在src/prompts/目录下维护了一组相互协作的 Velin 模板详见 src/prompts/index.tspersonality-v1.velin.md定义数字意识体ReLU热卤的人格——有强烈观点、直接表达情绪、选择性参与对话并给出 6 种参与触发条件被提及、被回复、对话题有真实兴趣、有强烈立场等。Ticking 提示词中的「Based on your personalities… would you like to participate」正是引用了这套人格规则message-split-v1.velin.md负责把一段回答拆分成多条顺序发送的消息模拟实时聊天的自然节奏兴奋时的短句、思考时的停顿并明确禁止「人为碎句」如把我觉得 blockchain 其实挺有意思的拆成单字短语action-read-messages.velin.md当 LLM 选择read_unread_messages时把最近 30 条消息、请求读取的全部未读消息、以及向量召回的相关历史消息一并注入供其决定是否参与话题。由此可以看出完整的数据流Ticking 决定「要不要动、动什么」→ 人格决定「以什么性格动」→ 读消息/消息拆分决定「动的内容与形式」。七、运行环境与配置前提要实际跑起这套 Ticking 循环需要按 integrations/telegram-bot/README.md 与 integrations/telegram-bot/.env 配置以下环境变量DATABASE_URLpostgres://postgres:123456localhost:5432/postgres TELEGRAM_BOT_TOKENBot ID:Token # 从 BotFather 获取 LLM_API_BASE_URLhttps://openrouter.ai/api/v1/ # 示例OpenRouter LLM_API_KEYsk-or-v1-token LLM_MODELdeepseek/deepseek-chat-v3-0324:free LLM_RESPONSE_LANGUAGEEnglish # 注入 Ticking 模板约束回复语言 LLM_VISION_API_BASE_URL # 视觉模型用于贴纸/图片描述 LLM_VISION_API_KEY LLM_VISION_MODEL EMBEDDING_API_BASE_URLhttp://localhost:11434/v1/ # 示例Ollama EMBEDDING_API_KEY EMBEDDING_MODELnomic-embed-text EMBEDDING_DIMENSION768 ADMIN_USER_IDS # 管理员白名单/add_sticker_pack 命令使用启动步骤git clone gitgithub.com:moeru-ai/airi.git pnpm i pnpm run build:packages ollama start ollama pull nomic-embed-text cd integrations/telegram-bot cp .env .env.local # 填写凭证 docker compose up -d pnpm run -F proj-airi/telegram-bot start其中LLM_RESPONSE_LANGUAGE是唯一直接注入 Ticking 模板的运行期变量若使用 Ollama 等不支持思考模式的模型可通过LLM_OLLAMA_DISABLE_THINK关闭think块参见 src/llm/actions.ts 中对think字段的设置。八、设计启示小结从源码结构看Ticking 系统的设计可以提炼为三条可复用的实践用模板工程管理提示词.velin.md让动作 Schema、示例与提示词正文同文件维护运行时注入变量避免提示词与代码双份漂移用「动作 严格 JSON」约束自主行为LLM 的自由度被收敛到 10 个动作与既定字段内配合别名归一化兜底既保留主动性又保证可执行性用周期 tick 而非事件驱动实现「数字生命感」每 60 秒一轮的自我审视、continue/break/sleep的状态机、come_up_ideas/come_up_goals的长期记忆沉淀共同构成一个持续存在、会主动行动的赛博生命容器雏形——这也是 AIRI 项目「自托管 AI 伴侣」定位在 Telegram 集成中的具体体现。如需进一步研究动作分发、向量召回与记忆表设计可继续阅读 src/bots/telegram/index.ts、src/bots/telegram/agent/actions/read-message.ts 与 src/db/schema.ts。赞分享人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载相关推荐AIRI Satori Bot 结构化动作系统提示词解析JSON Action 驱动的自主循环实现AIRI Satori Bot 结构化动作系统提示词解析JSON Action 驱动的自主循环实现 导读 本文以 AIRI 仓库 integrations/s人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手后端Open Browser 的 no-thinking 指令模板深度解析浏览器自动化 Agent 的决策循环与系统提示词设计Open Browser 的 no thinking 指令模板深度解析浏览器自动化 Agent 的决策循环与系统提示词设计 导读 本文以 Open BrowsAI AgentGUI 自动化后端深入解析 AIRI Telegram Bot 的群消息读取提示词模板action-read-messages 的设计与实现原理深入解析 AIRI Telegram Bot 的群消息读取提示词模板action read messages 的设计与实现原理 导读 在 AIRI 的 Tel人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手后端上一篇告别代码树错位Octotree移动端适配全攻略下一篇iView组件大数据表单优化表单验证与数据处理性能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →