前言:最近把手里一个闲置的 Telegram Bot 翻出来,给它接上了大模型,效果意外不错。整个过程踩了几个版本的坑,索性整理成文,分享给想动手的朋友。全程只讲技术,不涉及其他。

为什么选 Telegram Bot 做练手项目

如果你最近想做一个"能被真实用户使用"的 AI 应用,Telegram Bot 几乎是最低成本的路:

  • Bot 注册零成本,跟官方账号聊几句就完事

  • 天然解决前端问题——聊天窗口就是 UI,不用写页面

  • 消息推送、文件收发、按钮交互都是现成的

  • 部署简单,一台几十块一年的小鸡都能跑

所以这篇文章就带你从零写一个能接入大模型的 Bot,支持上下文记忆、Markdown 渲染,代码可以直接拿去改着用。

一、注册 Bot,拿到 Token

打开 Telegram,搜索官方账号 @BotFather,发送 /newbot,按提示给 Bot 起名字和用户名(用户名必须以 bot 结尾),它会回给你一串 Token,长这样:

7123456789:AAHq8xxxxxxxxxxxxxxxxxxxxxxx

这串 Token 就是 Bot 的"钥匙",后面所有请求都靠它。注意别提交到 Git 仓库,我一般是放 .env 文件里。

顺手再发一条 /setdescription 和 /setabouttext,把 Bot 的介绍填了,不然用户搜到的时候一脸空白。

二、环境准备

技术栈很简单:Python 3.10+,核心库就两个:

pip install python-telegram-bot openai python-dotenv

这里重点说一下坑:python-telegram-bot 从 v20 开始全面转向了异步,网上一大批老教程的同步写法直接跑不通,新手最容易卡在这。所以本文所有代码基于 v21+ 的异步写法,照着抄不会错。

三、先跑通一个 Echo Bot

不管做什么 Bot,第一步永远是先让消息"进得来、回得去"。新建 bot.py:

import os
from dotenv import load_dotenv
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes

load_dotenv()
TOKEN = os.getenv("BOT_TOKEN")

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("你好,我是活的,请吩咐。")

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text = update.message.text
    await update.message.reply_text(f"你刚说:{text}")

def main():
    app = Application.builder().token(TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    app.run_polling()

if __name__ == "__main__":
    main()

.env 文件:

BOT_TOKEN=7123456789:AAHq8xxxxxxxxxxxxxxxxxxxxxxx

跑起来:

python bot.py

去 Telegram 给你的 Bot 发消息,能原样回给你,说明链路通了。这个环节如果收不到消息,九成九是 Token 复制错了,或者公司网络把 api.telegram.org 的请求拦了,别怀疑人生。

四、接入大模型,让它真的会聊天

Echo 没意思,上主菜。这里用 OpenAI 兼容的 SDK 写法,这样不管你用的是官方 API 还是任何兼容服务,只改一个 base_url 就行:

from openai import AsyncOpenAI

ai_client = AsyncOpenAI(
    api_key=os.getenv("AI_API_KEY"),
    base_url=os.getenv("AI_BASE_URL"),  # 兼容接口地址
)

完整的聊天逻辑:收到消息 → 取这个用户的历史记录 → 拼成 messages → 调模型 → 回复。直接替换掉上面的 echo 函数:

# 用字典存每个用户的对话历史,key 是 user_id
# 正式项目请换 Redis 或数据库,重启就丢的内存版只适合自用
history_store = {}

SYSTEM_PROMPT = "你是一个友善、简洁的中文助手,回答控制在 300 字以内。"

async def chat(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.effective_user.id
    user_text = update.message.text

    history = history_store.setdefault(user_id, [
        {"role": "system", "content": SYSTEM_PROMPT}
    ])

    history.append({"role": "user", "content": user_text})

    # 防止历史无限膨胀,只保留最近 20 轮
    if len(history) > 41:
        history[:] = [history[0]] + history[-40:]

    try:
        resp = await ai_client.chat.completions.create(
            model="gpt-4o-mini",  # 换成你在用的模型名
            messages=history,
            temperature=0.7,
        )
        answer = resp.choices[0].message.content
        history.append({"role": "assistant", "content": answer})
        await update.message.reply_text(answer)
    except Exception as e:
        await update.message.reply_text(f"模型那边出问题了:{e}")

到这一步,Bot 已经能正经聊天了,记得上下文。发给朋友玩玩,基本都说不出"这是机器人在回"。

五、两个必做的体验优化

1. Markdown 渲染

模型回复默认是纯文本,像代码、列表这种格式显示得很丑。Telegram 支持 MarkdownV2,加一个参数就行:

await update.message.reply_text(
    answer,
    parse_mode="MarkdownV2"
)

但注意!MarkdownV2 对转义极其严格,.、(、! 这些符号不转义直接报错。实用做法是先渲染,失败了降级回纯文本:

try:
    await update.message.reply_text(answer, parse_mode="MarkdownV2")
except Exception:
    await update.message.reply_text(answer)

我反正是偷懒直接纯文本,看个人追求。

2. 打字状态提示

模型生成要好几秒,用户盯着空白对话框会以为挂了。加一行"正在输入"的状态,体验立刻不一样:

await context.bot.send_chat_action(
    chat_id=update.effective_chat.id,
    action="typing"
)

放在调模型之前即可。

六、部署:polling 还是 webhook

本地跑用 run_polling() 够了,简单粗暴。上生产的话两个选择:

  • polling:你的服务器主动轮询 Telegram 拿消息,代码简单,适合 QPS 低的 Bot。缺点是有秒级延迟。

  • webhook:Telegram 把消息推给你的回调地址,实时性好,但需要域名 + HTTPS 证书。

个人项目 polling 完全够用,别为了 webhook 那几百毫秒的优化给自己找事。一台便宜云服务器,nohup python bot.py & 挂后台,配合 systemd 或者 supervisor 保活,齐活。

总结

整件事拆开看就四步:BotFather 拿 Token → 装好 python-telegram-bot → 跑通收发 → 拼 messages 调模型。核心代码不到一百行,但已经是一个有上下文记忆的 AI 聊天机器人。

这玩意的扩展空间也很大:加 /clear 命令清空记忆、接语音消息转文字、做成群组里的问答助手、挂定时任务推送日报……都是在这个骨架上长出来的。

最后提醒三个最容易翻车的点:Token 别泄露、v20 之后必须异步写法、对话历史要设上限(不然 token 费用会教你做人)。

有问题评论区见,踩到新坑也欢迎来补充。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐