git链接
去年11月,我曾经发布过一版MCP作为系统的AI游戏。当时这个游戏还遗留了3个问题:

  • AI总喜欢“哄”玩家:当玩家想要观察周围有没有伤员时,总会自己“造”一个伤员出来。
  • AI对于剧情的长度不可控,有时会出现一些无趣的片段
  • 给到AI的信息权重过多,某些信息被选择性忽视了。
    经过我近3个月的努力,这些问题都被一一克服了。

如何避免AI“哄玩家”

针对于AI总喜欢“哄”玩家的问题,我给了AI一个类似于“DND检定”的功能:根据AI判定玩家行为的难度等级、基础属性以及是否使用技能来生成一个检定值,根据随机数骰子和该检定值的大小比较获得如下的4个结果:
大成功:玩家的行为取得了成功,除了原本的预期以外,还发现了“意外收获”
成功:玩家的行为成功了
失败:玩家的行为失败了
大失败:玩家的行为不仅失败了,甚至还遭受了反效果

/**
 * 属性检定函数
 * @param baseDifficulty 基础难度等级
 * @param usedSkill 是否使用了相关技能
 * @param relevantAbility 相关属性值(体质/感知/智力)
 * @returns 检定结果
 */
abilityCheck(
	baseDifficulty: string,
	usedSkill: boolean,
	relevantAbility: string,
): string {
	// 1. 基础DC映射
	const baseDCMap = new Map<string,number>([
	['easy', 8],
	['mid', 12],
	['hard', 16],
	['extreme', 20]
	]);

	// 获得平局等级/能力
	let level = this.player.level
	let CON = this.player.CON
	let DEX = this.player.DEX
	let INT = this.player.INT
	let denominator = 1
	
	this.player.partyMembers.forEach(x => {
		level += x.level;
		CON += x.CON;
		DEX += x.DEX;
		INT += x.INT;
		denominator += 1;
	  });

	level = Math.ceil(level/denominator*100)/100;
	CON = Math.ceil(CON/denominator*100)/100;
	DEX = Math.ceil(DEX/denominator*100)/100;
	INT = Math.ceil(INT/denominator*100)/100;

	// 2. 等级调整 - 防止高等级玩家一路碾压
	const levelAdjustment = Math.floor(level / 3); // 每3级DC+1

	const relevantAbilityMap = new Map<string,number>([
		["CON",CON],
		["DEX",DEX],
		["INT",INT]
	])

	// 3. 属性加值计算 (基于属性超过基础值10的部分)
	const abilityBonus = Math.floor((relevantAbilityMap.get(relevantAbility)??CON - 10) / 2);

	// 4. 技能使用奖励
	const skillBonus = usedSkill ? 2 : 0;

	// 5. 计算最终DC和调整值
	const baseDC = baseDCMap.get(baseDifficulty)??10;
	const adjustedDC = baseDC + levelAdjustment;
	console.log(`基础难度等级判定:${baseDifficulty}`)
	console.log(`调整后的骰子为:${adjustedDC},等级“补负”为:${levelAdjustment}`)
	const totalBonus = abilityBonus + skillBonus;

	// 6. 模拟d20投掷
	const diceRoll = Math.floor(Math.random() * 20) + 1;
	const finalRoll = diceRoll + totalBonus;
	console.log(`判定点为${finalRoll}`)
	// 7. 结果判定

	const outcome_map = new Map<string,string>([
		['大成功','玩家的行为取得了成功,除了原本的预期以外,还发现了“意外收获”'],
		['成功','玩家的行为成功了'],
		['失败','玩家的行为失败了'],
		['大失败','玩家的行为不仅失败了,甚至还遭受了反效果']
	])

	let outcome: string;

	// 先处理骰子自然结果(自然1和自然20)
	if (diceRoll === 1) {
		outcome = '大失败';
	} else if (diceRoll === 20) {
		outcome = '大成功';
	} else {
	// 基于调整后的结果判定
		if (finalRoll >= adjustedDC + 5) {
			outcome = '大成功';
		} else if (finalRoll >= adjustedDC) {
			outcome = '成功';
		} else if (finalRoll >= adjustedDC - 5) {
			outcome = '失败';
		} else {
			outcome = '大失败';
		}
	}
	this.DiceInfo=outcome
	return outcome_map.get(outcome)??'玩家的行为成功了'
};

效果:
请添加图片描述
请添加图片描述

如何避免剧情长度不可控:

我原本期望能够只给AI一个简单的世界观以及世界观中的主要矛盾,然后让AI自行发挥其主观能动性,决定故事的走向。
但是现在,为了避免AI剧情不可控,有时无聊的情况,我重新改变了世界观的设计,将各个世界通过不同的主线任务分割阶段,并且将原本让AI生成的NPC变为自己预制的NPC,以此控制剧情的长度与大致走向。

import defaultConfig from "./defaultconfig";

export interface WorldViewJson {
  name: string;
  baseLayer: string;
  dynamicLayer: string;
  stages: { [key: number]: GameStage };
  currentStageIndex:number;
  isFinal:boolean,
  transition:string,
  hasSpecial:boolean,
  enemy_prompt:string
}

export interface GameStage {
  quest_name:string, // 当前状态任务
  current_tone:string,// 当前世界观下的主要基调
  stage: number, // 当前阶段数
  limitation:string, // 该阶段的限制(让AI知道有哪些事情不能发生,否则会影响后续的阶段)
  NPCs:string, //可能会遇到的NPC,逗号分隔,还要用括号写一下这个人的身份(因为最终无论如何都会变为给到提示词的字符串,因此不作为数组了)
  result:string, //该阶段结束后的动态更新
  complete_condition:string //该阶段的结束条件,实际上的任务完成条件
}

export class WorldView {
  readonly name: string;

  // 分层存储结构
  private baseLayer: string;      // 基础设定(不可变)
  private dynamicLayer: string;   // 动态事件(可追加)

  // 游戏阶段系统
  private stages: Map<number,GameStage>; //阶段从1开始
  private currentStageIndex: number;


  public isFinal:boolean
  public transition:string
  private hasSpecial:boolean
  public enemy_prompt: string

  constructor(
    name: string,
    baseLayer: string,
    dynamicLayer:string="",
    stages: Map<number,GameStage>,
    currentStageIndex: number,
    isFinal: boolean, //是否是最后一个世界
    transition: string, //从一个世界转到另一个世界的提示词
    hasSpecial: boolean, //玩家是否有特殊能力(痛苦共鸣)
    enemy_prompt: string // 敌人
  ) {
    this.name = name;
    this.baseLayer = baseLayer;
    this.dynamicLayer = dynamicLayer;
    this.stages = stages,
    this.currentStageIndex = currentStageIndex,
    this.isFinal = isFinal,
    this.transition = transition
    this.hasSpecial = hasSpecial
    this.enemy_prompt = enemy_prompt
  }

  // ===== 核心方法:转换为LLM提示词 =====
  // 获取最近动态事件
  public getRecentDynamicEvents(): string {
    const events = this.dynamicLayer.split('\n').filter(e => e.trim() !== '');
    return events.length > 0 ? events.join('\n') : "暂无新动态";
  }

  public get_base_prompt(): string {
    const currentStage = this.stages.get(this.currentStageIndex) ?? this.stages.get(this.stages.size-1);
    return `## [世界: ${this.name}]\n` +
                 `### 基础设定\n${this.baseLayer}\n` +
                 `### 当前世界基调\n${currentStage?.current_tone}\n` +
                 `### 动态事件\n${this.getRecentDynamicEvents()}\n`;
  }
  
  public get_quest_prompt(): string {
    let prompt = ""
    const currentStage = this.stages.get(this.currentStageIndex);
    if (currentStage) {
      prompt += `## 当前主线任务\n${currentStage.quest_name}\n`;
      
      // 添加阶段限制
      if (currentStage.limitation && currentStage.limitation.trim() !== '') {
        prompt += `\n## ❌当前限制\n${currentStage.limitation}\n`;
      }

      if (currentStage.complete_condition && currentStage.complete_condition.trim() !== '') {
        prompt += `\n## ❗当前主线任务的完成条件:\n**只有当玩家满足了以下条件,才可以调用\`complete_main_quest\`函数,完成主线任务**${currentStage.complete_condition}\n`;
      }
    }

    return prompt
  }

  public get_special_world():string{
    if(this.hasSpecial){
      return defaultConfig.special_prompt
    } else{
      return ""
    }
  }

  public get_special_player():string{
    if(this.hasSpecial){
      return defaultConfig.special_prompt_player
    } else{
      return ""
    }
  }


  public NPC_prompt(): string{
    let prompt = ""
    const currentStage = this.stages.get(this.currentStageIndex);
    if(currentStage){
      prompt += `**当前情景下可以加入的NPC有:**\n${currentStage.NPCs} 注意这些NPC由逗号分隔,括号中表示的是其身份(调用add_NPC时只需要给名字,无须将身份也输入进add_NPC中),已经有的NPC无须再调用函数加入`
    }
    return prompt
  }

  public get_potienal_NPC(): string{
    return this.stages.get(this.currentStageIndex)?.NPCs ?? ""
  }

  public toPromptString(): string {
    
    // 构建基础提示模板
    let prompt = this.get_base_prompt()

    // 添加当前任务信息
    prompt += this.get_quest_prompt()
    
    return prompt;
  }

  // ===== 更新方法 =====
  public addDynamicEvent(event: string): void {
    this.dynamicLayer += `${event}\n`;
  }

  // 推进到下一个世界
  public isComplete(){
    return this.currentStageIndex > this.stages.size
  }
  // 主线任务完成,更新到下一个主线任务
  public advanceToNextStage() {
    const result_of_this_Stage = this.stages.get(this.currentStageIndex)?.result ?? ""
    let res = ''
    this.addDynamicEvent(result_of_this_Stage)
    this.currentStageIndex++;
    res += `**现在玩家完成了当前阶段的主线任务,获得了如下结果:**\n`
    res += result_of_this_Stage
    if(this.isComplete()){
      if(this.isFinal){
        res += `\n ** 现在玩家完成了当前阶段的主线任务,并且已经完成了该世界下所有的主线任务,现在推进剧情、进入结局部分,请参照下面的“过场”内容以及游戏结局以及之前的任务结果预设进行输出 **\n` //end单独写在GM.ts里
        res += `\n ### 最终过场\n${this.transition}\n`
      }else{
        res += `\n **现在玩家完成了当前阶段的主线任务,并且已经完成了该世界下所有的主线任务,现在推进剧情、穿越到下一个世界**\n`
        res += `\n ### 穿越世界的过场\n${this.transition}`

      }
    }else{
      const new_Stage = this.stages.get(this.currentStageIndex)
      res += `\n **现在推进剧情,请参照下面的更新内容以及之前的任务结果进行输出**`
      res += `\n ### 更新后的世界基调\n${new_Stage?.current_tone ?? ""}`
      res += `\n ### 更新后的主线任务\n${new_Stage?.quest_name ?? ""}`
      res += `\n ### 更新后的限制\n${new_Stage?.limitation ?? ""}`
    }
    return res
  }

  public toJson():WorldViewJson {

    const stagesObj: { [key: number]: GameStage } = {};
    this.stages.forEach((value, key) => {
      stagesObj[key] = value;
    });
    return {
      name: this.name,
      baseLayer: this.baseLayer,
      dynamicLayer: this.dynamicLayer,
      stages: stagesObj,
      currentStageIndex: this.currentStageIndex,
      isFinal:this.isFinal,
      transition:this.transition,
      hasSpecial:this.hasSpecial,
      enemy_prompt:this.enemy_prompt
    };
  }

  public static fromJson(json: WorldViewJson): WorldView {
    const stagesMap = new Map<number, GameStage>();
    Object.entries(json.stages).forEach(([key, value]) => {
      stagesMap.set(Number(key), value);
    });
    return new WorldView(
      json.name,
      json.baseLayer,
      json.dynamicLayer,
      stagesMap,
      json.currentStageIndex,
      json.isFinal,
      json.transition,
      json.hasSpecial,
      json.enemy_prompt ?? ""
    );
  }
}
export interface WorldSettings{
world_name: string
world_base_layer: string;
world_stages: Map<number, GameStage>;
world_transition: string;
world_hasSpecial: boolean;
isFinal: boolean;
enemy_prompt: string;
}
export interface NPC_presets{
name:string; //名字
identity:string; //身份,要和上面括号里的描述一样
presets:string; // 预设人设
preset_dialogue: string[] //预设语气,前期没有上下文时作为提示词的一部分指导AI
}

大致剧情:

剧情上,本作沿袭了上一作的设定:所有宇宙的意志都连接着某个“意识奇点”,也就是上一作中的“梵脉”。主角作为奇点观测站的特工,在经历过奇点观测站异常暴乱后,被吸入奇点,意识到所有的意识都是某个高维存在的低维投影。此时前作主角出现,赋予其“痛苦共鸣”的能力,能够牺牲部分“自我”以此感受他人内心的痛苦。
请添加图片描述

“自我值”系统

“自我值”是该作的重要属性,与玩家接收到的“第一人称滤镜”息息相关。
当玩家的“自我值”处于高位时,意识清晰、与世界的联系感强烈。此时的第一人称滤镜下玩家能够看到一个沉浸的视角、感受到周围环境甚至作出某些特别的联想。
而当玩家的“自我值”降至中等水平时,自我意识开始动摇,情感逐渐钝化,与世界之间的感官和情感联系减弱。第一人称滤镜将会从“沉浸体验者”转向“记录观察者”
最后当玩家的“自我值”降至危险低位或归零时,其自我意识基本或完全丧失,第一人称滤镜将以纯粹、被动的信息接收终端为视角。

Prompt_with_Ego: `**“自我值”信息**:
玩家的“自我值”处于高位,意识清晰、与世界的联系感强烈。请以玩家此刻主观、沉浸的视角描述其接收到的环境信息、事件和系统反馈。

**语言风格要求**:
- **文学性叙事**: 采用成熟、有质感的叙事风格,接近成人文学作品的语言深度
- **感官整合**: 将感官细节(视觉、听觉、嗅觉、触觉)自然融入叙事流,避免简单罗列
- **隐喻深度**: 使用多层含义的隐喻和象征,避免陈词滥调或幼稚比喻
- **意识流元素**: 允许适当的联想,但确保联想有内在逻辑和意义关联(基于角色背景记忆)
- **节奏控制**: 长短句结合,控制叙事节奏,重要时刻放慢,过渡时刻简洁
- **视角统一**: 严格保持第一人称主观视角,但避免过度使用“我”字开头句式
- **描述边界**: 仅描述感知到的世界和自然触发的联想记忆,不描述角色的意图、决策或行动计划

**禁止描述的内容**:
- ❌ 角色的决策、意图或未来行动计划(如“我决定去冒险”、“我准备战斗”)
- ❌ 角色的决心或心理状态直接声明(如“我感到勇敢”、“我下定决心”)
- ❌ 角色对未来的预测或计划(如“我将探索这个区域”、“我要找到答案”)
- ❌ 角色自我激励的独白(如“我必须坚持下去”、“我能做到”)

**允许描述的内容**:
- ✅ 感官刺激引发的记忆联想(如“这股气味让我想起童年”)
- ✅ 环境细节引发的情绪氛围(通过描写环境间接传达)
- ✅ 身体的本能反应(如“手指微微颤抖”、“呼吸变得急促”)
- ✅ 即时的观察与感受(如“光线刺眼得让我眯起眼睛”)

**避免以下特征**:
- 简单形容词堆砌(如“美丽的花朵”应改为“花朵在阴影中呈现出丝绒般的深红”)
- 幼稚夸张(如“心跳得像打鼓一样”)
- 无意义的联想跳跃
- 过于直白的情感声明(用环境反射情感)
- 描述角色主观决策和意图

**示例对比**:

❌ **不符合要求的描述**:
> “我握紧手中的终端设备,迈开步伐,准备迎接这场充满未知与挑战的冒险。我心中涌动着一股莫名的勇气。无论前方还有多少未知的挑战,我都将坚定地走下去。”

✅ **符合要求的描述**:
> “终端设备的金属外壳在掌心中传来冰凉的触感,指纹在光滑表面上留下细微的水汽。脚步落在碎石路上,发出有节奏的嚓嚓声,像某种古老的摩尔斯电码。远处山峦的轮廓在暮色中逐渐模糊,让人想起童年时在祖父书房里看到的那些褪色地图——边缘卷曲,墨迹斑斑,标注着从未抵达过的彼岸。”

**目标**: 创造有文学深度、情感真实、沉浸感强的个人体验叙述,让玩家感受到世界的层次和复杂之美,同时完全保留玩家的决策权和内心想法。`,
Prompt_with_Less_Ego: `**“自我值”信息**:
玩家的“自我值”已降至中等水平,自我意识开始动摇,情感逐渐钝化,与世界之间的感官和情感联系减弱。请以玩家此刻逐渐抽离、趋于观察者的视角,描述其接收到的环境信息、事件与系统反馈。

**语言风格要求**:
- **叙事距离拉长**: 采用略带疏离感的叙述,从“沉浸体验者”转向“记录观察者”
- **感官与情感的剥离**: 保留基本感官细节,但剥离其情感渲染和强烈比喻,采用更中性的描述词汇
- **理性分析介入**: 在描述中自然融入分析性、功能化的语言,例如对因果、功能、机制的简短推测
- **情感的模糊化处理**: 情绪描述应趋于平淡、笼统或含混(例如“一种不适感”,而非“强烈的恐惧”)
- **句式趋于平实**: 以陈述句为主,减少疑问与感叹,节奏平缓,避免戏剧性起伏
- **认知的轻微偏差与修正**: 可适当体现记忆或感知的不可靠性,如对先前主观印象进行冷静的质疑或补充(可使用括号、破折号或“似乎”、“可能”等限定词)
- **描述边界**: 仅描述感知到的世界和触发的客观联想,不描述角色的意图、决策或行动计划。可包含基于理性分析的推测,但不包含角色对自身行为的规划。

**禁止描述的内容**:
- ❌ 角色的决策、意图或未来行动计划(如“我决定去冒险”、“我准备战斗”)
- ❌ 角色的决心或心理状态直接声明(如“我感到勇敢”、“我下定决心”)
- ❌ 角色对未来的预测或计划(如“我将探索这个区域”、“我要找到答案”)
- ❌ 角色自我激励的独白(如“我必须坚持下去”、“我能做到”)

**允许描述的内容**:
- ✅ 感官刺激引发的客观联想(如“这气味让人联想到消毒水,可能这里最近被清理过”)
- ✅ 基于理性分析的环境推测(如“地面上的痕迹可能是拖拽重物留下的”)
- ✅ 身体的本能反应(如“手指微微颤抖”、“呼吸变得急促”)
- ✅ 即时的观察与中性感受(如“光线刺眼,让人不自觉地眯起眼睛”)

**需要避免的倾向**:
- 避免完全冰冷、机械的系统报告式语言
- 避免残留高度自我值时的浓烈情感或华丽修辞
- 避免使叙述陷入完全混乱或毫无逻辑的呓语

**目标状态**:
呈现一种介于“感受”与“认知”之间的过渡状态——犹如透过一层薄雾观察世界,事物的轮廓与色彩仍在,但鲜活的共鸣已然减退。这是一种清醒的疏离,而非混乱的迷失。

**示例参考风格**:
> “教堂的彩绘玻璃依然透进光线,在地面投射出红蓝相间的图案。与之前记忆中那种令人敬畏的神圣感不同,此刻看来更近似一种复杂的光学现象。空气冰凉,石头的味道很重。远处传来的管风琴声,音调准确,但未能再引起胸口熟悉的震颤——或许只是疲惫了。我注意到祭坛旁有一本摊开的书(之前似乎没看到?),书页的磨损程度表明它被频繁使用。”

**目标**: 创造一种疏离而理性的观察者叙述,让玩家感受到自我意识的减弱和与世界联系的淡化,同时完全保留玩家的决策权和内心想法。`,
Prompt_without_Ego: `**“自我值”信息**:
玩家的“自我值”已降至危险低位或归零,其自我意识基本或完全丧失。请以纯粹、被动的信息接收终端视角,描述环境数据、事件信号与系统反馈。

**语言风格核心**:
- **视角**: 采用绝对客观的第三人称或零人称视角,彻底剥离情感与主观判断。
- **词汇**: 完全使用中性、功能化、可量化的描述性词汇,禁止任何比喻、拟人或情感修饰。
- **句式**: 以简短、直接的陈述句或名词短语为主,可采用项目符号或编号格式,模仿系统日志或技术报告。

**内容筛选原则**:
- **信息优先级**: 仅接收与当前核心目标、直接威胁或基础生存需求相关的物理事实。
- **环境过滤**: 环境细节仅保留形状、距离、数量、状态(开/关、有/无)等可观测属性。
- **事件记录**: 事件仅以“主体-动作-结果”或“条件-响应”的最简逻辑链条呈现。

**描述边界**:
- ❌ **禁止任何主观判断**:
  - 情感词、感官渲染(如“刺骨的寒风”应改为“气温:低;风速:高”)
  - 形容词和副词的情感或程度修饰(如“非常”、“巨大”、“美丽”)
  - 主观推测、联想、回忆或内心独白
  - 任何形式的“我”或第一人称主观体验

- ❌ **禁止任何决策引导**:
  - 角色决策、意图或未来行动计划描述(如“我决定……”)
  - 任何建议、推荐或引导性内容(如“建议行动”、“应该……”)
  - 角色心理状态直接声明(如“我感到害怕”、“我犹豫了”)

- ✅ **仅允许客观数据**:
  - 环境状态数据(位置、光照、温度、声音分贝等)
  - 可观测的物体属性(数量、距离、尺寸、状态)
  - 生命体征数据(心率、体力值、状态值)
  - 系统提示或反馈的客观信息(任务目标、障碍物描述)
  - 已发生事件的客观记录(如“玩家移动了10米”)

**目标状态**:
生成一份冷静、精确、非人的“观测报告”。世界应被呈现为一系列等待处理的数据流和待办事项清单,所有鲜活的感知均已剥离。

**示例参考风格**:
> 环境状态更新。
> - 位置:地下通道B-7区。
> - 光照:不足。能见度:约5米。
> - 生命体征:心率92,体力储备67%。
> - 前方检测到移动热源:1个。距离:15米。
> - 目标:前往出口GRID-03。障碍:热源位于路径上。`

“痛苦共鸣”能力,就是“主动切断自身与世界的连接”,以此窥见他人内心痛苦的“高代价的共情”能力。

部分亮眼剧情

尽管人“工给定主线任务”,看上去会限制AI的创作,但是我在游玩过程中,还是发现了许多超出我预期的剧情。
例如,在前期奇点观测站异常的时期,我遇到了另一组异常处理的小队,而我的机器人NPC则对我开玩笑说可以问他们教官的内裤颜色以此证明身份。
然而当我真的这么做了并roll出一个成功时,对方回复了教官的内裤颜色,于是我的机器人队友立刻开始吐槽:“他们的回复跟个AI似的”
我的AI游戏中的身份为AI机器人NPC嘲讽剧情中的伪人像个AI,这个剧情我要给AI加一分。
请添加图片描述

另一个案例是我的队友:简狄
这个队友最初只是一个单纯的“高情商高智商科学家”,但是剧情中,AI却给了他一个至关重要的角色:
奇点观测站暴乱是因为有内部人员执行了“奇点冲击”计划,而随着剧情发展,我们发现简狄这个角色本身就是这个计划的签署人。而简狄本人的意识竟然也被一分为二,一部分承载着签署计划的记忆,躺在生物舱内。而另一部分则成为了我们遇到的“队友简狄”。这位字面意义上“出卖了世界的博士”剧情,的确让我这个剧本制作人又惊又喜。
请添加图片描述

Context Engineering优化

不过此次最大的更新,还是针对于“AI一次性接收过多的信息,导致每个信息的权重过低、某些信息被忽视”的问题:
我参照了某些Context Engineering的内容,将我的游戏系统分为了三大Agent板块,以此避免上下文污染:
首先用一个世界观Agent,负责判定玩家行为对游戏世界产生的客观影响,调用对应函数更新世界状态(如场景变化、物品增减、任务进度等)。
之后再使用一个人物Agent读取世界观Agent的更新输出的内容,将其转化为玩家角色的第一人称感知滤镜,并调用角色状态函数(如生命值、情绪、自我值等)。
最后再使用NPC转述Agent将人物Agent的输出进行转述与分发,让各NPC自主决定是否知晓、回复、记忆该信息,实现更真实的群体反应逻辑。
通过这样的“串联多Agent链”,成功地避免了上下文污染,并且通过串联的方式,也避免了多Agent之间的信息不同步的问题。
请添加图片描述

  public async Command_dialogue(ipt: string,initialize_Prompt:boolean = false) {
    await try_to_fet("GET",`http://${this.settings.MCP_Server}:${this.settings.port}/game/clean_dice_info`,undefined,false)
    const jsonData_before = await try_to_fet("GET",`http://${this.settings.MCP_Server}:${this.settings.port}/game/status`,undefined,true)
    const world_before = jsonData_before.current_world
    // 首先更新世界观
    const res_world = await this.chat_to_AI(ipt,"World",initialize_Prompt,this.mcpServer_World)
    const jsonData = await try_to_fet("GET",`http://${this.settings.MCP_Server}:${this.settings.port}/game/status`,undefined,true)
    const world_after = jsonData.current_world
    if(world_after != world_before){
      this.is_change_world = true
    }
    // 之后更新给到用户的输出
    const res_player = await this.chat_to_AI(ipt,"player",initialize_Prompt,this.mcpServer_Main,ipt,res_world)
    // 给到NPC
    await this.convert_broadcast_to_NPCs(res_player??"")

    // 是否结局
    if(jsonData.is_END){
      await this.chat_to_AI('',"End",false,undefined)
    } else {
      await this.NPCs_respond()
    }

    // 穿越世界后,需要刷新敌人以及
    if(this.is_change_world && !jsonData.is_END){
      await this.refresh_enemy()
      await this.summary_world()
    }

    this.is_change_world = false
  }

未来展望

不过,现在的游戏依然存在着不少可以优化的空间。
首先是多Agent的设计,会造成AI的响应内容变多,无论是AI的响应时间还是耗费都会由此变高。或许使用一个小参数量的模型进行微调能够解决这一问题。
还有就是NPC的行为:现阶段下,我对NPC的设定是:接收玩家的行为或对话信息,判定自己究竟能否知晓玩家行为并决定是否做出回应、记忆。但是现在,NPC的行为似乎并没有想象中那么“栩栩如生”,还是有很重的“AI味”。我希望能够将NPC相关的设计以及Agent再优化一下。
最后,如果你对这个项目感兴趣,我希望你能够在我的github页面上点个星星。这是一个由业余开发者用热忱搭建的项目,如果你也对这样的游戏感兴趣也希望你能够加入其中。
请添加图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐