news 2026/9/26 18:07:06

Trae实战:半天从0到1搭建AI海龟汤游戏,文生图+推理记忆全流程配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Trae实战:半天从0到1搭建AI海龟汤游戏,文生图+推理记忆全流程配置

1. 为什么我想用 Trae 做一个会画画的 AI 海龟汤

海龟汤这个玩法,本质上是一个“信息不对称”的推理游戏:出题人手里有完整故事(汤底),玩家只能通过“是/否/无关”的提问,一点点把汤面还原出来。传统玩法靠主持人,一旦主持人不在,游戏就散了。我一直在想,能不能让 AI 当这个主持人,而且它还能记住你问过的每一个问题,最后根据汤底画一张插画,把氛围拉满。

这个想法落地需要三块能力:一是 AI 能稳定生成汤面和判定提问,二是推理过程要落库,不能刷新页面就失忆,三是文生图接口要能根据汤底出图。如果纯手写,光是 Flask 路由、SQLite 表结构、Prompt 编排和文生图调用,就够折腾两三天。我这次用 Trae 做主力开发工具,把样板代码和调试环节压缩掉,半天跑通了从 0 到 1 的雏形。

这篇不是“Trae 有多神”的软文,而是一份可跟做的配置记录。你会看到 Flask + SQLite 的项目骨架、TaoToken 统一 Key 的 config 片段、文生图接口调用、推理历史落库的可复制代码,以及本地启动和验证步骤。适合想用 AI 编程工具快速做小项目、又不想在 API 接入上反复踩坑的人。核心检索词就三个:Trae、AI 海龟汤、文生图 + 推理记忆。

2. 先解决 API 通道:TaoToken 统一 Key 的前置配置

做这类小项目,最烦的不是业务逻辑,而是“这个模型用这家 Key,那个模型用那家 Key”。海龟汤里我要调对话模型做提问判定,还要调文生图模型出插画,如果每家都单独申请、单独配环境变量,config 会变得很乱。我这次用 TaoToken 做统一通道,一个 Key 走对话和文生图,config 里只维护一份 base_url 和 api_key。

TaoToken 的官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址后面不加 UTM 参数。你注册后在控制台创建 API Key,然后把它写进项目的.env里,不要硬编码进代码。

我试过把 Key 直接写在app.py里,结果提交 Git 时差点泄露,后来改成环境变量才安心。下面是我实际用的 config 片段,放在config.py里,Flask 启动时加载:

# config.py import os from dotenv import load_dotenv load_dotenv() class Config: # TaoToken 统一通道 TAOTOKEN_API_KEY = os.getenv("TAOTOKEN_API_KEY", "") TAOTOKEN_BASE_URL = "https://taotoken.net/api" # 对话模型:用于提问判定和汤面生成 CHAT_MODEL = "gpt-4o-mini" # 文生图模型:用于汤底插画 IMAGE_MODEL = "dall-e-3" # SQLite 配置 SQLALCHEMY_DATABASE_URI = "sqlite:///haiguitang.db" SQLALCHEMY_TRACK_MODIFICATIONS = False # 会话密钥 SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-change-me")

对应的.env文件长这样,你只需要填自己的 Key:

TAOTOKEN_API_KEY=sk-你的TaoTokenKey SECRET_KEY=随便一串随机字符

这里有个细节:TaoToken 的 base_url 是https://taotoken.net/api,但不同 SDK 对路径拼接方式不一样。OpenAI 官方 SDK 会在 base_url 后面自动加/v1/chat/completions,所以如果你用openai这个包,base_url 要写成https://taotoken.net/api/v1才能拼对。我一开始只写到/api,结果请求 404,排查了十几分钟才反应过来。下面第 3 节的代码里我会用完整路径,避免你踩同样的坑。

注意:API Key 只放在服务端环境变量里,前端 HTML/JS 里绝对不能出现。海龟汤是多人玩的,一旦 Key 泄露,别人可以刷你的额度。

3. 可复制配置:Flask + SQLite 项目骨架与文生图调用

项目结构我按“能跑起来”优先来排,不搞过度分层。Trae 帮我生成了大部分样板,我手动调整了 AI 调用和落库部分。目录如下:

haiguitang/ ├── app.py ├── config.py ├── models.py ├── ai_service.py ├── image_service.py ├── requirements.txt ├── .env └── templates/ └── index.html

先装依赖,requirements.txt内容:

flask flask-sqlalchemy python-dotenv openai requests

然后models.py定义三张表:游戏记录、问答历史、插画记录。推理记忆的关键就在QAHistory这张表,每次玩家提问和 AI 判定都落一条,这样刷新页面也能恢复上下文。

# models.py from flask_sqlalchemy import SQLAlchemy from datetime import datetime db = SQLAlchemy() class Game(db.Model): id = db.Column(db.Integer, primary_key=True) difficulty = db.Column(db.String(20)) # 普通/一般/困难 genre = db.Column(db.String(20)) # 清汤/红汤/黑汤 surface = db.Column(db.Text) # 汤面 bottom = db.Column(db.Text) # 汤底 status = db.Column(db.String(20), default="playing") created_at = db.Column(db.DateTime, default=datetime.utcnow) class QAHistory(db.Model): id = db.Column(db.Integer, primary_key=True) game_id = db.Column(db.Integer, db.ForeignKey("game.id")) question = db.Column(db.Text) answer = db.Column(db.String(20)) # 是/否/无关 created_at = db.Column(db.DateTime, default=datetime.utcnow) class Illustration(db.Model): id = db.Column(db.Integer, primary_key=True) game_id = db.Column(db.Integer, db.ForeignKey("game.id")) image_url = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow)

ai_service.py负责两件事:生成汤面和判定提问。判定时我会把该局所有历史问答拼进 Prompt,这就是“记住推理过程”的实现方式。注意 base_url 我写的是https://taotoken.net/api/v1,配合 openai SDK 使用。

# ai_service.py from openai import OpenAI from config import Config from models import db, QAHistory client = OpenAI( api_key=Config.TAOTOKEN_API_KEY, base_url="https://taotoken.net/api/v1" ) def generate_story(difficulty, genre): prompt = f"生成一个海龟汤故事,难度{difficulty},类型{genre}。" prompt += "输出格式:第一行汤面,第二行汤底。汤面要离奇,汤底要合理。" resp = client.chat.completions.create( model=Config.CHAT_MODEL, messages=[{"role": "user", "content": prompt}], temperature=0.9 ) text = resp.choices[0].message.content.strip() lines = [l for l in text.split("\n") if l.strip()] surface = lines[0].replace("汤面:", "").strip() bottom = lines[1].replace("汤底:", "").strip() if len(lines) > 1 else "" return surface, bottom def judge_question(game, question): history = QAHistory.query.filter_by(game_id=game.id).all() history_text = "\n".join([f"问:{h.question} 答:{h.answer}" for h in history]) prompt = f"汤底是:{game.bottom}\n已知问答:\n{history_text}\n" prompt += f"玩家新问题:{question}\n只回答 是/否/无关 三个词之一。" resp = client.chat.completions.create( model=Config.CHAT_MODEL, messages=[{"role": "user", "content": prompt}], temperature=0.2 ) return resp.choices[0].message.content.strip()

image_service.py调文生图。这里我用 TaoToken 的 images 接口,同样走统一 Key。注意文生图比对话慢,建议加超时和重试。

# image_service.py import requests from config import Config def generate_illustration(bottom_text): url = "https://taotoken.net/api/v1/images/generations" headers = { "Authorization": f"Bearer {Config.TAOTOKEN_API_KEY}", "Content-Type": "application/json" } payload = { "model": Config.IMAGE_MODEL, "prompt": f"悬疑插画风格,氛围阴郁,主题:{bottom_text[:200]}", "n": 1, "size": "1024x1024" } resp = requests.post(url, json=payload, headers=headers, timeout=60) resp.raise_for_status() data = resp.json() return data["data"][0]["url"]

app.py把路由串起来,核心是/ask接口:先落库问题,再调 AI 判定,再把答案落库,最后返回给前端。

# app.py from flask import Flask, request, jsonify, render_template from config import Config from models import db, Game, QAHistory, Illustration from ai_service import generate_story, judge_question from image_service import generate_illustration app = Flask(__name__) app.config.from_object(Config) db.init_app(app) with app.app_context(): db.create_all() @app.route("/") def index(): return render_template("index.html") @app.route("/start", methods=["POST"]) def start(): data = request.json surface, bottom = generate_story(data["difficulty"], data["genre"]) game = Game(difficulty=data["difficulty"], genre=data["genre"], surface=surface, bottom=bottom) db.session.add(game) db.session.commit() return jsonify({"game_id": game.id, "surface": surface}) @app.route("/ask", methods=["POST"]) def ask(): data = request.json game = Game.query.get(data["game_id"]) answer = judge_question(game, data["question"]) qa = QAHistory(game_id=game.id, question=data["question"], answer=answer) db.session.add(qa) db.session.commit() return jsonify({"answer": answer}) @app.route("/reveal", methods=["POST"]) def reveal(): data = request.json game = Game.query.get(data["game_id"]) game.status = "finished" db.session.commit() image_url = generate_illustration(game.bottom) ill = Illustration(game_id=game.id, image_url=image_url) db.session.add(ill) db.session.commit() return jsonify({"bottom": game.bottom, "image_url": image_url}) if __name__ == "__main__": app.run(debug=True, port=5000)

前端index.html我写得比较朴素,一个开始按钮、一个提问输入框、一个揭晓按钮,用 fetch 调上面三个接口。Trae 在这里帮我补了 CSS 和事件绑定,省了不少时间。

4. 验证请求:本地启动与成功结果确认

代码写完后,先确认环境变量生效。在项目根目录执行:

python -c "from config import Config; print(Config.TAOTOKEN_BASE_URL, bool(Config.TAOTOKEN_API_KEY))"

如果输出https://taotoken.net/api True,说明 Key 读到了。然后启动 Flask:

python app.py

看到Running on http://127.0.0.1:5000就说明服务起来了。接下来用 curl 验证三个接口,不用等前端写完。

先测开始游戏:

curl -X POST http://127.0.0.1:5000/start \ -H "Content-Type: application/json" \ -d '{"difficulty":"一般","genre":"红汤"}'

成功的话返回类似:

{"game_id":1,"surface":"一个男人走进酒吧,点了一杯水,酒保却拔枪指着他,男人说了声谢谢就走了。"}

拿到game_id后测提问判定:

curl -X POST http://127.0.0.1:5000/ask \ -H "Content-Type: application/json" \ -d '{"game_id":1,"question":"男人是口渴吗?"}'

返回{"answer":"否"}就说明对话通道通了。你可以连续问几个问题,然后查 SQLite 确认历史落库:

sqlite3 instance/haiguitang.db "select question, answer from qa_history;"

能看到多条记录,说明推理记忆生效。最后测揭晓和文生图:

curl -X POST http://127.0.0.1:5000/reveal \ -H "Content-Type: application/json" \ -d '{"game_id":1}'

返回里带image_url就成功了。文生图通常要 10 到 30 秒,如果 curl 卡住别急着杀,等超时设置生效。我实测下来,1024x1024 的图大概 15 秒左右返回。

5. 本篇常见错排查

第一个高频错误是 401 Unauthorized。原因通常是.env没加载,或者 Key 前后有空格。检查Config.TAOTOKEN_API_KEY是否为空,以及.env文件是否在项目根目录。另外注意load_dotenv()要在读取环境变量之前调用,我把它放在config.py顶部就是为了这个。

第二个是 404 Not Found,路径拼错。如果你用 openai SDK,base_url 写https://taotoken.net/api/v1;如果你用 requests 直接调,完整路径是https://taotoken.net/api/v1/images/generations。少写/v1或者多写斜杠都会 404。这个坑我在第 2 节提过,这里再强调一次。

第三个是 SQLite 表不存在。Flask-SQLAlchemy 默认把数据库放在instance/目录下,如果你在app.py里没写with app.app_context(): db.create_all(),第一次请求就会报no such table。确认这行代码在启动时执行了。

第四个是文生图超时。requests.post默认没有超时,网络慢时会一直挂起。我在image_service.py里加了timeout=60,你也可以加到 90。如果还是超时,检查 prompt 是否过长,我截断到 200 字符就是为了避免这个问题。

第五个是推理记忆串局。judge_question里查询历史时一定要带game_id过滤,否则会把其他局的问答也拼进 Prompt,导致 AI 判定混乱。这个 bug 我在测试时遇到过,表现为新开一局后 AI 回答明显受上一局影响,加上过滤就好了。

提示:如果你在 Trae 里让 AI 帮你改代码,改完记得重新跑一遍 curl 验证,不要只看代码逻辑觉得对就跳过。AI 生成的代码有时会漏掉db.session.commit(),导致数据没落库。

6. 接下来怎么走:从雏形到可玩

半天跑通雏形后,我建议你先别急着加功能,而是把“一局完整流程”走顺:开始、提问五到八轮、揭晓、看到插画。这个过程里你会自然发现哪些 Prompt 需要调,比如汤面太直白、判定太模糊。调 Prompt 比加功能更影响体验。

如果你想把项目继续做下去,下一步可以接 Coding Plan 做长期迭代,把前端交互和多人房间补上。需要先确认 Key 和通道的,可以去 API Keys 页面创建,接入细节看接入文档。想先单独验证模型对话效果的,用模型对话页面直接试 Prompt,不用每次改代码重启 Flask。这几个入口我都放在下面,按你的需要选:

  • 创建和管理 Key:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
  • 模型对话验证:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
  • 长期编码与 Agent:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
  • 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite

最后说一个我踩过的坑:Trae 生成代码很快,但它不知道你的 Key 额度还剩多少。文生图接口调用几次后如果开始报错,先去控制台看用量,别一味改代码。把额度监控和错误处理加上,这个项目才算真正能给别人玩。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 18:03:10

Video2X免费开源:AI超分与帧插值修复老视频实战指南

老视频最让人头疼的不是画质差,而是那种“糊得没法看,又舍不得删”的尴尬。十年前用手机拍的婚礼片段、早期数码相机录的家庭影像、甚至从老DVD里扒出来的素材,分辨率往往只有480P甚至更低,放到现在的大屏设备上,满屏都…

作者头像 李华
网站建设 2026/9/26 18:02:13

SQLite 工具类实战:用反射把 Cursor 封装到 VO 对象

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华