时间: 2026年3月下旬 - 4月上旬

在项目选题正式确定为“StoryEcho:基于大模型的沉浸式互动叙事平台”并完成详细任务书撰写后,我们 FateWeaver 团队进入了第二个关键阶段——技术预研与项目框架雏形搭建。这段时间的核心目标是学习项目所需的技术栈,并产出一个可运行、包含核心交互逻辑的最小可行性产品框架,为后续成员并行开发奠定基础。


目录

一、技术选型确认与团队学习

1.1 前端技术栈学习

1.2 后端技术栈学习

1.3 LangGraph 与 AI Agent 架构学习

1.4 状态管理思想学习

二、框架雏形搭建:从反复调试到确立雏形

2.1 线下共同讨论与各自尝试

2.2 确定最终可运行框架

三、现有项目雏形功能解析

3.1 统一启动入口 (run.py)

3.2 后端 API 服务 (app.py)

3.3 故事引擎 (story_engine.py)

3.4 前端核心功能 (app.js + index.html)

四、总结与下一阶段计划


一、技术选型确认与团队学习

在正式编码之前,我们花了一周左右的时间进行技术调研和学习。以下是大模型根据我们项目书内容推荐的技术框架。

虽然团队对 Web 开发有一定基础,但要构建一个涉及 AI Agent 协作、复杂状态管理、长程上下文处理 的互动叙事系统,仍存在明显的知识盲区。以下是我们在这一阶段重点学习和了解的技术内容。

1.1 前端技术栈学习

经过讨论,我们决定采用 Vue 3 作为前端框架,主要基于以下考量:

  • 组件化开发:互动叙事平台需要大量的弹窗组件(角色创建、结局画廊、任务日志等),Vue 的单文件组件模式能够帮助我们更好地组织代码结构。

  • 响应式数据绑定:游戏状态(如角色属性、背包物品、NPC好感度)需要实时反映在界面上,Vue 的响应式系统可以让 UI 自动跟随数据变化,减少手动 DOM 操作的复杂度。

  • Composition API:相较于 Options API,Composition API 能够更灵活地组织逻辑代码,特别是当单个组件逻辑变得复杂时(如主游戏界面同时管理叙事窗口、侧边栏状态、快捷操作等多个模块)。

我们还学习了如何在不使用构建工具的情况下,通过 CDN 方式快速引入 Vue 和周边库(如 Axios)。这种方式降低了初期环境配置的复杂度,让我们能够更快地验证想法。

1.2 后端技术栈学习

后端方面,我们选择了 FastAPI 框架,主要学习内容包括:

  • 异步请求处理:由于 AI 生成剧情可能需要数秒的响应时间,异步框架能够避免请求阻塞,提升并发处理能力。

  • 自动 API 文档生成:FastAPI 内置的 Swagger UI 可以自动生成接口文档(/docs 端点),这对团队协作调试非常有帮助。

  • CORS 跨域配置:由于前后端分离开发,前端运行在 localhost:8080,后端运行在 localhost:8000,需要正确配置 CORS 中间件才能让浏览器允许跨域请求。

1.3 LangGraph 与 AI Agent 架构学习

这是本项目的核心技术难点。传统上,如果直接让一个大模型同时负责“理解用户意图、计算数值变化、生成文学描述”,容易出现以下问题:

  • 指令漂移:模型可能在生成剧情时忽略了之前设定的人物状态(如明明没血了还写勇猛战斗)。

  • 数学不准确:大模型不擅长精确的数值计算(如 HP 扣减、概率判定)。

  • 上下文遗忘:长程交互中容易忘记早期埋下的伏笔。

通过查阅相关资料,我们了解到 LangGraph 是一个用于构建多智能体协作流程的框架。它的核心思想是:

  • 将复杂任务拆分为多个独立的“节点”,每个节点负责一个单一职责(如意图识别、逻辑判定、文本生成)。

  • 通过“有向无环图”定义节点之间的流转顺序。

  • 使用“状态对象”在节点间传递数据。

我们设想未来可以将 StoryEcho 的叙事流程建模为以下 Agent 节点:

  1. 意图解析 Agent:接收用户自由文本输入,识别用户的真实意图(想攻击、想对话、想探索等),输出结构化指令。

  2. 逻辑裁判 Agent:根据结构化指令和当前角色属性,进行数值演算(如攻击是否命中、造成多少伤害、好感度如何变化)。

  3. 叙事导演 Agent:基于逻辑裁判的结果,生成符合当前情境的沉浸式剧情描述。

这种架构的优势在于:将“逻辑”与“文学”解耦。逻辑裁判专注于精确的数学计算和规则判定,叙事导演专注于根据结果生成优美连贯的文字。两者通过状态对象协作,既能保证游戏规则的严谨性,又能发挥大模型的创造力。但是由于项目成本和技术难度,最终落实可行性不确定。

1.4 状态管理思想学习

我们还学习了如何设计一个结构化的“游戏状态对象”。在互动叙事中,需要追踪的信息非常多样:

  • 角色属性(HP、MP、力量、智力等)

  • 背包物品列表

  • NPC 好感度

  • 任务进度

  • 对话历史

我们学习了将这些信息统一封装在一个 gameState 对象中的设计模式。这样,无论是前端展示还是后端逻辑处理,都可以通过读写同一个状态对象来完成,避免了数据分散导致的不一致问题。


二、框架雏形搭建:从反复调试到确立雏形

完成初步技术学习后,我们进入了框架搭建阶段。这一过程并非一帆风顺,而是经历了多次尝试和调整。

2.1 线下共同讨论与各自尝试

在4月初的一周里,我们采取了“先各自探索、后集中整合”的工作模式。每位成员都在自己的电脑上尝试搭建一套可运行的前后端连接环境,并借助 DeepSeek、ChatGPT 等大模型来辅助解决遇到的问题。

大家遇到的问题主要集中在:

  • 环境配置问题:Python 依赖包的版本兼容性(特别是 uvicorn 和 fastapi 的版本匹配)、Vue CDN 资源加载失败等。

  • 跨域请求失败:前端请求后端接口时浏览器报 CORS 错误,需要正确配置 FastAPI 的 CORSMiddleware

  • 前后端数据格式不一致:后端返回的 JSON 结构与前端期望的字段名不匹配,导致页面渲染空白。

  • 启动脚本设计:需要一个统一的启动脚本 run.py,能够一键启动后端服务并提示前端访问方式。

在各自调试的过程中,我们不断通过大模型获取帮助。例如,当遇到 CORS 问题时,我们将错误信息粘贴给 DeepSeek,它会给出配置代码示例;当遇到无法运行,环境不适配时,大模型给出对应解决方案。

2.2 确定最终可运行框架

经过多次线下碰头讨论和代码比对,我们最终选定了团队中可以运行、代码结构最清晰的一套版本作为项目框架。这个框架具备以下特点:

  • 一键启动:运行 python run.py 即可启动后端服务,控制台会清晰打印访问地址和 API 文档地址。

  • 前后端解耦:前端纯静态文件,可通过 Live Server 或 Python HTTP Server 独立运行,便于调试。

  • Mock 数据支持:即使不接入大模型 API,系统也能基于内置的简化规则返回响应,方便前端开发独立进行。

  • 完整的状态流转:从故事选择到角色创建(Mock 版本),再到主游戏循环,整个流程已经打通。


三、现有项目雏形功能解析

目前的代码框架已经成功支撑起了两个核心界面:故事大厅 和 主叙事对话界面。以下结合代码详细介绍已实现的主要功能。

3.1 统一启动入口 (run.py)

为了方便团队成员和后续演示,我们编写了统一的启动脚本。该脚本负责启动 FastAPI 后端服务,并在控制台输出清晰的访问指引。

#!/usr/bin/env python
import subprocess
import sys
import os

def main():
    print("=" * 50)
    print("StoryEcho 沉浸式互动叙事平台")
    print("=" * 50)
    
    # 启动后端
    print("\n🚀 启动后端服务...")
    backend_process = subprocess.Popen(
        [sys.executable, "-m", "uvicorn", "backend.app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"],
        cwd=os.path.dirname(os.path.abspath(__file__))
    )
    
    print("✅ 后端服务已启动: http://localhost:8000")
    print("📖 API文档: http://localhost:8000/docs")
    
    # 提示前端启动方式
    print("\n🌐 前端访问方式:")
    print("   1. 使用Live Server打开 frontend/index.html")
    print("   2. 或使用Python HTTP服务器: cd frontend && python -m http.server 8080")
    print("   3. 访问: http://localhost:8080")
    
    print("\n按 Ctrl+C 停止服务...")
    
    try:
        backend_process.wait()
    except KeyboardInterrupt:
        print("\n正在停止服务...")
        backend_process.terminate()
        backend_process.wait()
        print("服务已停止")

if __name__ == "__main__":
    main()

3.2 后端 API 服务 (app.py)

后端基于 FastAPI 提供了以下核心接口:

(1)获取故事列表 (GET /api/stories)

@app.get("/api/stories")
async def get_stories():
    stories = []
    for sid, template in engine.story_templates.items():
        stories.append({
            "story_id": sid,
            "title": template["title"],
            "genre": template["genre"],
            "background": template["background"],
            "difficulty": "中等"
        })
    return {"stories": stories}

该接口从 StoryEngine 的故事模板库中读取所有可用剧本,返回给前端展示。目前内置了“迷雾森林”和“赛博2077”两个故事。

(2)开始游戏 (POST /api/story/start)

@app.post("/api/story/start")
async def start_story(req: StartRequest):
    session_id = str(uuid.uuid4())
    state = engine.start_story(req.story_id, req.user_id)
    active_sessions[session_id] = state
    return {
        "session_id": session_id,
        "state": {
            "messages": state["messages"],
            "attributes": state["attributes"],
            "inventory": state["inventory"],
            "task_progress": state["task_progress"],
            "turn_count": state["turn_count"]
        }
    }

该接口接收故事 ID 和用户 ID,创建一个新的游戏会话,返回唯一的 session_id 和初始游戏状态。

(3)处理用户行动 (POST /api/story/action)

@app.post("/api/story/action")
async def process_action(req: ActionRequest):
    if req.session_id not in active_sessions:
        raise HTTPException(status_code=404, detail="会话不存在")
    
    state = active_sessions[req.session_id]
    new_state = engine.process_action(state, req.user_input)
    active_sessions[req.session_id] = new_state
    
    result = {
        "state": {
            "messages": new_state["messages"],
            "attributes": new_state["attributes"],
            "inventory": new_state["inventory"],
            "task_progress": new_state["task_progress"],
            "turn_count": new_state["turn_count"]
        },
        "session_id": req.session_id
    }
    
    if new_state.get("ending_triggered"):
        result["ending"] = new_state["ending_triggered"]
    
    return result

这是核心交互接口。接收用户输入文本和会话 ID,调用 StoryEngine.process_action() 处理逻辑,返回更新后的游戏状态。注意,目前 StoryEngine 是基于规则匹配的简化版本,下一阶段将被替换为调用大模型 API 的 LLMStoryEngine

3.3 故事引擎 (story_engine.py)

目前的故事引擎是一个基于关键词匹配的简化实现,用于模拟游戏逻辑,方便前端独立开发和调试。

    def process_action(self, state: Dict, user_input: str) -> Dict:
        # 添加用户消息
        state["messages"].append({"role": "user", "content": user_input})
        state["turn_count"] += 1
        
        # 解析意图
        text = user_input.lower()
        response = ""
        
        # 根据不同意图生成响应
        if "探索" in text or "周围" in text:
            response = self.handle_explore(state)
        elif "背包" in text or "物品" in text:
            response = self.handle_inventory(state)
        elif "攻击" in text or "战斗" in text:
            response = self.handle_attack(state)
        elif "治疗" in text or "药水" in text:
            response = self.handle_heal(state)
        elif "状态" in text or "属性" in text:
            response = self.handle_status(state)
        else:
            response = self.handle_general(state, text)
        
        # 添加状态显示
        hp = state["attributes"]["hp"]
        hp_emoji = "❤️" if hp > 70 else "💛" if hp > 30 else "💔"
        status = f"\n\n【状态】{hp_emoji} HP:{hp}/100 | 💪力量:{state['attributes']['strength']} | 🧠智力:{state['attributes']['intelligence']}"
        
        full_response = response + status
        
        # 添加AI回复
        state["messages"].append({"role": "assistant", "content": full_response})
        state["current_description"] = full_response
        
        # 检查结局
        if state["attributes"]["hp"] <= 0:
            state["ending_triggered"] = "bad_ending"
        elif state["turn_count"] >= 15:
            state["ending_triggered"] = "good_ending"
        
        return state

各处理函数实现了基本的游戏逻辑:

    def handle_explore(self, state: Dict) -> str:
        discoveries = [
            ("你发现了一些野果!生命值恢复了10点。", {"hp": 10}),
            ("你在地上捡到了20枚金币。", {"money": 20}),
            ("你找到了一瓶治疗药水!", {}),
            ("你发现了一条新的小径,但没有什么收获。", {})
        ]
        desc, changes = random.choice(discoveries)
        for key, val in changes.items():
            if key in state["attributes"]:
                state["attributes"][key] = min(100, state["attributes"][key] + val)
        if "治疗药水" in desc and "治疗药水" not in state["inventory"]:
            state["inventory"].append("治疗药水")
        return f"🔍 {desc}"

3.4 前端核心功能 (app.js + index.html)

前端采用 Vue 3 的 Composition API 组织代码,核心响应式数据如下:

const gameStarted = ref(false);          // 是否已开始游戏
const stories = ref([]);                 // 故事列表
const selectedStory = ref(null);         // 选中的故事
const gameState = ref({                  // 游戏状态
    scene_id: '',
    current_description: '',
    messages: [],
    attributes: {},
    inventory: [],
    relationships: {},
    task_progress: {},
    turn_count: 0
});
const userInput = ref('');               // 用户输入
const isLoading = ref(false);            // 加载状态
const sessionId = ref('');               // 会话ID

(1)故事选择与游戏启动

const selectStory = (story) => {
    selectedStory.value = story;
    showCharacterCreation.value = true;
};

const startGameWithCharacter = async (characterData) => {
    isLoading.value = true;
    try {
        const response = await axios.post(`${API_BASE}/api/story/start`, {
            story_id: selectedStory.value.story_id,
            user_id: 'user_' + Date.now(),
            character: characterData
        });
        
        sessionId.value = response.data.session_id;
        gameState.value = response.data.state;
        gameStarted.value = true;
    } catch (error) {
        console.error('开始游戏失败:', error);
        // 降级到 Mock 模式
        startMockGame(selectedStory.value, characterData);
    } finally {
        isLoading.value = false;
    }
};

(2)发送用户行动

const sendAction = async () => {
    if (!userInput.value.trim() || isLoading.value) return;
    
    const action = userInput.value;
    userInput.value = '';
    isLoading.value = true;
    
    // 将用户消息添加到界面
    gameState.value.messages.push({ role: 'user', content: action });
    
    try {
        const response = await axios.post(`${API_BASE}/api/story/action`, {
            user_input: action,
            session_id: sessionId.value
        });
        
        gameState.value = response.data.state;
        
        if (response.data.ending) {
            ending.value = true;
            endingType.value = response.data.ending;
        }
    } catch (error) {
        console.error('发送行动失败:', error);
        mockResponse(action);
    } finally {
        isLoading.value = false;
    }
};

(3)快捷操作

const quickAction = (action) => {
    userInput.value = action;
    sendAction();
};

前端界面通过 v-for 渲染消息列表,通过 v-model 绑定输入框,整体交互流程已经完整打通。


四、总结与下一阶段计划

经过这段时间的努力,我们完成了从技术学习到框架搭建的关键一步。目前的成果如下:

  1. 技术学习:团队成员对 Vue 3 Composition API、FastAPI 异步开发、LangGraph 多智能体架构思想有了基本了解,为后续深入开发打下了基础。

  2. 框架雏形:产出了一个可运行的基线版本,包含故事选择、主游戏循环、状态追踪等核心功能。

  3. 代码规范:确立了前后端数据交互的 JSON 格式规范,为后续并行开发提供了统一标准。

下一阶段的核心目标是:接入真实的 AI 大脑,验证对话流。

具体计划包括:

  1. 接入 API Key:在 app.py 中编写新的 LLMStoryEngine 类,替换目前基于关键词匹配和 if-else 规则的模拟回复逻辑。该类将调用大模型 API(如 DeepSeek 或 OpenAI),并将模型返回的非结构化文本解析为符合我们 gameState 结构的 JSON 数据。

  2. 验证对话流:测试在接入大模型后,目前的 JSON 状态结构是否能被 AI 稳定生成和解析。重点关注 attributes 中的数值变化(如 HP 减少、金币增加)和 inventory 的物品增删逻辑是否准确,确保“逻辑裁判”的精确性不被大模型的“文学生成”所干扰。

通过亲手搭建这个框架,我们深刻体会到“逻辑与叙事分离”架构的必要性。目前的框架为后续接入大模型、实现真正的 AI 驱动互动叙事体验做好了充分准备。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐