第9.5章 把状况和日志交给AI来编写Portal代码

第9.5章 把状况和日志交给AI来编写Portal代码

0 把状况和日志交给AI来编写Portal代码

人类负责告诉AI「想做什么」和「发生了什么」,代码和原因调查交给AI

用TypeScript编写Portal脚本时,实际写程序这件事很大一部分可以交给AI。
尤其是查找函数名、匹配类型、添加日志、缩小原因范围,这些工作AI很擅长。

相应地,也有一些信息必须由人类提供。

  • 想做什么。
  • 说的是哪张地图、哪个对象、哪个时机。
  • 实机中发生了什么。
  • 日志里出现了什么。

这些信息如果很含糊,AI即使写出看起来像样的代码,也容易不符合Portal现场的实际情况。
反过来,只要把现象和日志清楚交给AI,它就会成为相当可靠的伙伴。

本章以可以使用BF6 Portal TypeScript MCP的AI环境为例,整理一种不仅适用于Codex,也适用于其他AI的「编码对话」推进方式。

1 与AI的分工

先把适合交给AI的事和人类应该观察的事分开。

角色 负责方 内容
决定目标 人类 例如「想在玩家面前生成Apple」
提供条件 人类 地图名、想使用的对象名、触发时机
编写代码 AI TypeScript实现、Portal API调用、添加日志
查询SDK AI 使用MCP或类型定义确认函数名、类型、enum候选
实机测试 人类 注册到Portal并在游戏内确认行为
阅读日志 AI和人类 提供 PortalLog.txt 并缩小原因范围
修正代码 AI 根据日志提出假设并修改代码

人类不需要一开始就记住所有API。
但是,「想让什么发生」和「实际上发生了什么」必须由人类观察并交给AI。

程序可以让AI来写。
不过,如果交给AI的状况说明也很粗糙,对话就会开始迷路。
这里偷懒,之后反而更麻烦。

2 什么是BF6 Portal TypeScript MCP

BF6 Portal TypeScript MCP是一个MCP服务器,可以让AI开发环境查询Portal SDK的类型信息。

仓库在这里。

https://github.com/link1345/bf6-portal-typescript-mcp

如果告诉AI「不清楚的Portal API请先用MCP查询再写」,AI就可以一边参考SDK信息一边写代码,而不需要人类每次都去搜索 index.d.ts

MCP就像交给AI的一本词典。
与其把它理解成人类直接操作、用来背API的工具,不如把它理解成告诉AI「不清楚的地方就用MCP查」的工具。

不过,仅靠MCP并不能知道实机中的行为。
游戏里变成了什么样、日志里出现了什么,仍然需要人类交给AI。

如果在MCP的设置或使用方法上卡住了,也可以先直接问Codex的AI。

我想设置bf6_portal_typescript_mcp。
请看一下现在的设置画面和错误,确认还缺什么。
如果Portal API有不清楚的地方,请先用MCP查询后再回答。

问AI时,把错误信息、设置画面里输入的内容、SDK文件夹的位置一起交给它,会更快推进。

3 在Codex App中设置MCP服务器

这里是使用Codex App时的设置示例。
如果使用其他AI开发环境,请按该环境的MCP设置方法进行替换。

首先从Codex App左下角菜单打开设置。

打开Codex App设置

打开设置画面后,在左侧菜单选择「MCP 服务器」,然后按「添加服务器」。

Codex App的MCP服务器设置

在自定义MCP设置画面中,按如下方式输入。

项目 输入示例
名称 bf6-portal-typescript-mcp
类型 STDIO
启动命令 npx
参数1 bf6-portal-typescript-mcp@latest
参数2 mcp
参数3 --sdk_path
参数4 /path/to/bf6-portal-sdk

Codex App中MCP服务器的设置方法

请把 /path/to/bf6-portal-sdk 替换成自己的Portal SDK位置。
大致来说,指定能看到 code/types/mod/index.d.ts 的位置。

设置后,如果Codex的工具列表中可以使用 bf6_portal_typescript_mcp,就准备完成了。

不能正常工作时的确认

设置后仍然不能正常工作时,按下面的顺序确认。

  • npx 是否可用。
  • --sdk_path 指定的文件夹中是否有 code/types/mod/index.d.ts
  • 设置后是否重启了Codex App。
  • Codex的工具列表中是否出现了 bf6_portal_typescript_mcp
  • 服务器名称是否简短、容易识别。

如果服务器名称不好理解,AI可能会迷惑该使用哪个MCP。
OpenAI的Docs MCP说明中也提到,如果希望AI使用特定MCP,可以对AI明确说明,或写入 AGENTS.md

https://developers.openai.com/learn/docs-mcp

Codex App的设置也稍微看一下

Codex App的Settings中,不仅可以调整MCP,也可以调整外观、人格、Custom instructions等。
如果想用日语,可以在自定义指示中写入下面这样的内容。这样说明会偏向日语,同时代码和日志不容易被破坏。

请用日语说明。
代码、函数名、日志请保留原始英文。

如果连代码、函数名、日志也被翻译成日语,Portal侧可能无法直接使用。
说明文字用日语,代码和API名保持英文,这样分开比较稳妥。

4 最开始交给AI的请求

第一次请求时,要具体写出想创建的现象。
代码细节可以交给AI,但目标、条件、可用工具要提供。

请编写一个BF6 Portal的TypeScript程序,在玩家生成时,在玩家面前生成「Apple_01」对象。
可以使用bf6_portal_typescript_mcp服务器,所以如果有不清楚的Portal内容,请使用该MCP服务器查询。

这个请求提供了三类信息。

  • 想做的事:在生成的玩家面前生成对象。
  • 想使用的东西:Apple_01
  • 调查方式:Portal API有不清楚的地方就使用MCP。

如果已经知道地图,也从一开始就写进去。

地图是Mirak Valley。
如果Apple_01的RuntimeSpawn候选会因地图而不同,请先用MCP确认后再选择。

让AI写代码时,不要只说「请写代码」。
把「不清楚的Portal API请查询」也一起说出来,这是诀窍。

5 第一次没有完成也不是失败

AI最初写出的代码中,Apple有时不会显示。
这可能是AI完全写错了,也可能只是缺少Portal侧的加载信息或实机时机信息。

这里重要的是,不要停在「看不见」。
让AI加入下面这样的确认用代码。

  • OnGameModeStarted 中加入 console.log
  • 在画面右上角显示通知。
  • OnPlayerDeployed 中加入 console.log
  • 把生成坐标输出到日志。

例如,在 OnGameModeStarted 中加入这样的确认。

export function OnGameModeStarted(): void {
    console.log("AppleInFront: OnGameModeStarted");
    mod.SetSpawnMode(mod.SpawnModes.AutoSpawn);
    mod.DisplayNotificationMessage(mod.Message("AppleInFront loaded"));
}

如果画面右上角出现 AppleInFront loaded,说明该代码已经被Portal读取。
如果没有出现,在怀疑Apple坐标之前,先怀疑注册的 Script.ts 或构建后的 dist/Script.ts

6 把日志交给AI并请求解决

Portal日志会输出到如下位置。

%LOCALAPPDATA%\Temp\Battlefieldâ„¢ 6\PortalLog.txt

无法运行时,把这个日志交给AI。
重要的不是感想,而是日志本身。

不好的交法如下。

Apple没有显示。请修复。

这样无法判断是加载失败、事件未触发、坐标错误,还是对象不对。

好的交法如下。

Apple没有显示。
`%LOCALAPPDATA%\Temp\Battlefieldâ„¢ 6\PortalLog.txt` 中只有下面这条日志。

[UTC 2026-04-30 23:38:28] Mod started

请修改代码,让我们能够确认Portal是否读取了代码、事件是否触发。
必要时请用bf6_portal_typescript_mcp查询Portal API。

有了这些信息,AI就能判断「首先应该确认代码是否被读取」。

7 把状态和现象一起交给AI

有时,代码已经被读取,但Apple仍然看不见。

地图是Mirak Valley,所以Tungsten应该是正确的。
日志如下。
我个人比较在意日志里的「0, -0.3499999940395355, 2」。
这看起来不像全局坐标。

[UTC 2026-04-30 23:47:31] Mod started
[UTC 2026-04-30 23:47:48] QuickJS: console.log: AppleInFront: OnGameModeStarted
[UTC 2026-04-30 23:47:48] QuickJS: console.log: AppleInFront: OnPlayerDeployed
[UTC 2026-04-30 23:47:48] QuickJS: console.log: Apple spawn position: 0, -0.3499999940395355, 2

这种交法相当好。
AI可以把状况拆开来思考。

  • OnGameModeStarted 正在运行。
  • OnPlayerDeployed 也正在运行。
  • Apple spawn position 已经输出。
  • 但是坐标看起来接近原点。
  • 可能是在出击后立刻读取时,士兵坐标或朝向还没有确定。

这种情况下,把下面的修正交给AI处理。

  • OnPlayerDeployed 改成 async
  • 在读取士兵状态前使用 await mod.Wait(...) 稍等。
  • 不以 EyePosition 为基准,而以 GetPosition 为基准。
  • 输出 Player positionPlayer eye positionPlayer facing directionApple spawn position
  • 把等待时间做成容易修改的常量。

人类不需要完全猜中原因。
只要把「这个值可疑」「看起来不像全局坐标」这样的观察交出去,AI就更容易确定调查方向。

8 完成例:在玩家面前生成Apple

在Mirak Valley生成 Apple_01 时,完成形如下。
它会在出击后等待3秒,读取玩家的位置和朝向,然后在前方2米处生成稍大的Apple。

const APPLE_DISTANCE_METERS = 2.0;
const APPLE_VERTICAL_OFFSET_METERS = 0.2;
const APPLE_SCALE = mod.CreateVector(3, 3, 3);
const APPLE_ROTATION = mod.CreateVector(0, 0, 0);
const SOLDIER_STATE_DELAY_SECONDS = 3.0;

function getAppleSpawnPosition(player: mod.Player): mod.Vector {
    const playerPosition = mod.GetSoldierState(player, mod.SoldierStateVector.GetPosition);
    const facingDirection = mod.Normalize(mod.GetSoldierState(player, mod.SoldierStateVector.GetFacingDirection));

    return mod.Add(
        mod.Add(playerPosition, mod.Multiply(facingDirection, APPLE_DISTANCE_METERS)),
        mod.CreateVector(0, APPLE_VERTICAL_OFFSET_METERS, 0)
    );
}

function logVector(label: string, vector: mod.Vector): void {
    console.log(`${label}: ${mod.XComponentOf(vector)}, ${mod.YComponentOf(vector)}, ${mod.ZComponentOf(vector)}`);
}

export function OnGameModeStarted(): void {
    console.log("AppleInFront: OnGameModeStarted");
    mod.SetSpawnMode(mod.SpawnModes.AutoSpawn);
    mod.DisplayNotificationMessage(mod.Message("AppleInFront loaded"));
}

export async function OnPlayerDeployed(eventPlayer: mod.Player): Promise<void> {
    console.log("AppleInFront: OnPlayerDeployed");

    await mod.Wait(SOLDIER_STATE_DELAY_SECONDS);

    logVector("Player position", mod.GetSoldierState(eventPlayer, mod.SoldierStateVector.GetPosition));
    logVector("Player eye position", mod.GetSoldierState(eventPlayer, mod.SoldierStateVector.EyePosition));
    logVector("Player facing direction", mod.GetSoldierState(eventPlayer, mod.SoldierStateVector.GetFacingDirection));

    const spawnPosition = getAppleSpawnPosition(eventPlayer);
    logVector("Apple spawn position", spawnPosition);

    mod.SpawnObject(
        mod.RuntimeSpawn_Tungsten.Apple_01,
        spawnPosition,
        APPLE_ROTATION,
        APPLE_SCALE
    );

    mod.DisplayNotificationMessage(mod.Message("Apple spawned"), eventPlayer);
}

成功后,Apple会显示在出击玩家的面前。

完成后的Apple生成画面

9 交给AI的信息模板

想让AI修正Portal代码时,按下面的形式交给它,对话会更快。

想做的事:
想在生成的玩家面前生成Apple_01。

环境:
地图是Mirak Valley。
可以使用bf6_portal_typescript_mcp,所以Portal API有不清楚的地方请查询。

当前状态:
代码已经注册到Portal。
游戏内看不到Apple。

日志:
`%LOCALAPPDATA%\Temp\Battlefieldâ„¢ 6\PortalLog.txt` 的内容如下。

...

在意的点:
生成坐标是 `0, -0.3499999940395355, 2`,看起来不像全局坐标。

请求:
请添加用于切分原因的日志,并进行必要的代码修正。

这种形式既适用于Codex,也适用于其他AI。
交给AI的不需要是完美推理。
目标、状态、日志、在意的点就够了。

10 初学者容易卡住的地方

设置了MCP,但AI没有使用

即使可以使用某个工具,AI也不一定每次都会自动选择最合适的工具。
Portal API有不清楚的地方时,要在请求中明确写出「请用MCP查询」。

可以使用bf6_portal_typescript_mcp。
如果Portal API或enum有不清楚的地方,请先用MCP确认后再写代码。

如果每次都写很麻烦,也可以把同样的方针写进项目的 AGENTS.md

看不懂英文错误

错误信息不需要勉强自己翻译。
可以直接原样贴给AI,然后让它用日语说明原因和下一步确认步骤。

出现了下面的错误。
我不太擅长英语,请用日语说明原因和确认步骤。
但是,代码和函数名请保持英文。

...

错误信息省略太多,原因也可能一起消失。
即使日志很长,也最好保留最开始的错误、最后的错误,以及自己执行的操作。

Codex用英语回复

可以在Codex App的Settings或Custom instructions中指定使用日语回答。
不过,也要写明Portal代码、API名、日志、文件名要保持英文。

可以翻译成日语的是说明。
如果代码内部也被翻译成日语,复制使用时可能会坏掉。

害怕很快达到Rate limit

OpenAI的Rate limit会按请求数、Token数等多个维度计算,并且也会因模型而不同。

https://developers.openai.com/api/docs/guides/rate-limits

fast 不是「可以随便大量消耗」的模式。
因为回复很快,短时间内更容易连续发出很多请求。再加上如果每次都完整粘贴很长的日志或代码,Token消耗会变大,也就更容易感觉接近限制。

如果觉得不安,可以这样拆开。

  • 日志先贴看起来相关的部分。
  • 不要一次请求大规模修正,而是先请求「先只看原因」。
  • 不要每次都贴完整代码,而是重点交出「上次之后改了哪里」和「出现的错误」。
  • 不要反复贴同样的说明,而是整理成目标、现象、日志摘录、请求的形式。

快速模式很方便,但不要把它当成节约模式,这样更安全。

11 这种推进方式的注意点

  • 可以以前提「让AI写代码」来推进。
  • 人类交出「目标」「现象」「日志」。
  • 日志从 %LOCALAPPDATA%\Temp\Battlefieldâ„¢ 6\PortalLog.txt 获取。
  • 在能使用MCP的环境中,让AI查询不清楚的Portal API。
  • 即使AI不能使用MCP,交出日志和现象的会话形式也一样。
  • 如果不能运行,按读取、事件触发、坐标、Prefab的顺序切分。
  • 实机看到的结果才是最终判断。不要只凭AI的说明就当作完成。

结论

  • Portal的TypeScript代码,很大程度上可以交给AI。
  • 人类的工作是交出想做什么、发生了什么、日志里出现了什么。
  • BF6 Portal TypeScript MCP可以作为让AI查询Portal SDK的词典。
  • Codex App的设置只是使用MCP的一例,编码对话的基本方式在其他AI中也一样。
  • 只要交出目标、状态、日志、在意的点,和AI的修正循环就会快很多。