Java Spring项目升级实战:给Codex提供MCP服务

为什么要把 Spring 项目升级成 MCP 服务

最近在使用 Codex 维护 Java 项目时,我发现一个很明显的限制:Codex 可以读取当前代码仓库,但它并不知道测试环境里的实时库存、采购订单状态、供应商履约数据,也不能直接复用公司系统已经实现的查询和操作能力。

最简单的处理方式,是人工把数据复制到对话里。但数据很快会过期,复制过程还容易遗漏字段。另一种方式,是临时给 Codex 数据库账号或让它调用内部 REST API,这会把表结构、鉴权、接口拼装和业务规则都暴露给客户端,风险也很难控制。

MCP(Model Context Protocol)提供了一个更合适的边界:Spring 项目继续负责业务规则、事务、权限和数据访问,只把少量经过设计的能力暴露成 MCP tools;Codex 负责理解任务、选择工具、填写参数,并把结果用于代码分析、故障排查或业务验证。

本文用供应链系统做示例,把一个已有的 Spring Boot 项目升级为 Codex 可以调用的 MCP 服务。最终提供两个工具:

  • query_inventory_snapshot:查询某个仓库中 SKU 的库存快照,只读。
  • create_replenishment_draft:创建补货草稿,不自动审批、不自动下单。

Codex调用Spring MCP服务架构

这次升级最重要的原则是:MCP 层只是适配器,不重新实现业务。Controller、定时任务和 MCP tool 都应该调用同一套 Application Service,库存口径、权限规则和事务边界仍然只有一份。

先选传输方式:为什么使用 Streamable HTTP

Codex 当前可以连接两类 MCP 服务:

  • STDIO:Codex 通过命令启动本地进程,并使用标准输入输出通信,适合只在开发机运行的工具。
  • Streamable HTTP:MCP 服务作为独立应用运行,Codex 通过 URL 连接,适合已有 Spring Web 项目、团队共享服务和远程部署。

本文升级的是已经运行在服务器上的 Spring Boot 项目,因此选择 Streamable HTTP。它不会要求 Codex 启动 Java 进程,也便于复用 Spring Security、Actuator、日志、限流和现有发布体系。

不要再把旧的 HTTP+SSE 教程当成默认方案。Spring AI 当前文档把 Streamable HTTP 作为新的 HTTP 传输方式,Spring MVC 和 WebFlux 都有对应 starter。普通 Servlet 项目使用 WebMVC;项目本身已经是响应式链路时再选择 WebFlux。

第一步:确认版本兼容关系

示例采用下面的基线:

组件 示例版本 说明
Java 21 适合作为当前项目基线,示例会使用 record
Spring Boot 4.x Spring AI 2.0.x 支持 Spring Boot 4.0.x 和 4.1.x
Spring AI 2.0.0 使用当前正式版 BOM 管理依赖
传输方式 Streamable HTTP Codex 通过 /mcp URL 连接

如果现有项目还是 Spring Boot 3.x,不要为了增加一个 MCP tool 就直接把整个项目升级到 Boot 4。更稳的做法是先查看对应 Spring AI 1.1.x 文档,选择与现有 Boot 版本兼容的发布线;如果项目本来就计划升级 Boot 4,再把 Jakarta、Jackson 3、Spring Framework 7 等迁移内容作为独立任务完成。

可以先运行下面的命令确认项目当前版本:

1
2
3
java -version
./mvnw help:evaluate -Dexpression=project.parent.version -q -DforceStdout
./mvnw dependency:tree -Dincludes=org.springframework.ai

版本没有对齐时,常见结果不是启动失败,而是运行到工具扫描或 JSON 序列化时才出现 ClassNotFoundExceptionNoSuchMethodError。因此先确定版本矩阵,再写业务代码。

第二步:增加 Spring AI MCP 依赖

项目已经使用 Spring Boot parent 的情况下,可以在 pom.xml 中导入 Spring AI BOM,再添加 WebMVC MCP Server starter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>2.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
</dependencies>

BOM 的作用是统一 Spring AI 内部模块和 MCP Java SDK 的版本,不要给每个 Spring AI 依赖单独写版本号。依赖增加后先做一次构建:

1
./mvnw clean test

如果项目以前使用过早期 MCP 依赖,还要检查两个变化:

  • 旧的 MCP starter 命名已经调整为 spring-ai-starter-mcp-* 风格。
  • Spring AI 2.0 的注解包是 org.springframework.ai.mcp.annotation.*,不要继续引用旧的 org.springaicommunity.mcp.annotation.*

第三步:配置 MCP Server

application.yml 中增加 MCP Server 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server:
port: 8088

spring:
ai:
mcp:
server:
name: supply-chain-mcp
version: 1.0.0
type: SYNC
protocol: STREAMABLE
instructions: >
本服务提供供应链库存查询和补货草稿能力。优先调用只读查询工具;
创建草稿前必须向用户展示 SKU、仓库、数量和原因;不得把草稿描述为已审批或已下单。
capabilities:
tool: true
resource: false
prompt: false
completion: false
streamable-http:
mcp-endpoint: /mcp
keep-alive-interval: 30s

这里有几个值得注意的配置:

第一,protocol 必须是 STREAMABLE,Codex 配置中的 URL 则需要写到完整的 /mcp 路径。

第二,示例只暴露 tools,所以关闭了暂时不用的 resource、prompt 和 completion。能力越少,服务行为越容易理解,攻击面也越小。

第三,instructions 是服务级说明。Codex 初始化 MCP 连接后可以读取它,用来理解跨工具都适用的规则。最重要的约束应该放在开头,并让前 512 个字符可以独立表达意思。它是给模型的使用指南,不是权限系统,真正的权限和业务校验仍然必须写在服务端。

第四步:把现有业务能力包装成 MCP Tool

假设原项目已经有 InventoryQueryService,负责按统一口径查询可用量、锁定量和在途量。MCP 层只做参数校验、调用 Service 和结果裁剪。

先定义一个稳定的返回对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.supplychain.mcp;

import java.math.BigDecimal;
import java.time.Instant;

public record InventorySnapshot(
String skuCode,
Long warehouseId,
BigDecimal onHandQuantity,
BigDecimal lockedQuantity,
BigDecimal availableQuantity,
BigDecimal inTransitQuantity,
Instant refreshedAt) {
}

再新增 MCP 工具适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.example.supplychain.mcp;

import com.example.supplychain.inventory.InventoryQueryService;
import com.example.supplychain.replenishment.ReplenishmentApplicationService;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpToolParam;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
public class SupplyChainMcpTools {

private final InventoryQueryService inventoryQueryService;
private final ReplenishmentApplicationService replenishmentApplicationService;

public SupplyChainMcpTools(
InventoryQueryService inventoryQueryService,
ReplenishmentApplicationService replenishmentApplicationService) {
this.inventoryQueryService = inventoryQueryService;
this.replenishmentApplicationService = replenishmentApplicationService;
}

@McpTool(
name = "query_inventory_snapshot",
description = "查询指定SKU在指定仓库的实时库存快照,返回现存、锁定、可用和在途数量。只读,不修改库存。",
generateOutputSchema = true,
annotations = @McpTool.McpAnnotations(
readOnlyHint = true,
destructiveHint = false,
idempotentHint = true,
openWorldHint = false))
public InventorySnapshot queryInventorySnapshot(
@McpToolParam(description = "SKU编码,例如 SKU-10086", required = true)
String skuCode,
@McpToolParam(description = "仓库ID,例如 3101", required = true)
Long warehouseId) {

if (!StringUtils.hasText(skuCode)) {
throw new IllegalArgumentException("skuCode不能为空");
}
if (warehouseId == null || warehouseId <= 0) {
throw new IllegalArgumentException("warehouseId必须是正整数");
}

var snapshot = inventoryQueryService.querySnapshot(
skuCode.trim().toUpperCase(), warehouseId);

return new InventorySnapshot(
snapshot.skuCode(),
snapshot.warehouseId(),
snapshot.onHandQuantity(),
snapshot.lockedQuantity(),
snapshot.availableQuantity(),
snapshot.inTransitQuantity(),
snapshot.refreshedAt());
}
}

Spring AI 会扫描 @McpTool,根据方法参数生成输入 JSON Schema,并把方法注册为 MCP tool。@McpToolParam 的描述会直接影响 Codex 如何填写参数,所以不要只写“ID”或“编码”,要说明业务含义、格式和示例。

readOnlyHintdestructiveHintidempotentHint 等注解是客户端提示,有助于 Codex 判断工具风险,但服务端不能依赖这些提示保障安全。无论模型怎么调用,方法内部都要执行参数校验,Application Service 仍要执行数据权限和业务校验。

为什么不直接把 Repository 暴露成工具

下面这类工具看起来很灵活,实际上应该禁止:

1
2
3
execute_sql(sql)
call_any_url(url, body)
update_order(tableName, where, values)

它们把业务边界交给了模型,还会带来 SQL 注入、越权查询、任意网络访问和不可审计修改等问题。MCP tool 应该使用业务语言,例如“查询库存快照”“创建补货草稿”,并让每个工具只有一个明确职责。

第五步:增加一个受控的写工具

真实项目最终可能需要写操作,但不应该一上来就让 Codex 直接提交采购单。可以先提供“创建草稿”这样的可回退操作,把审批和正式下单留在人类流程中。

先定义一个只返回草稿编号、状态和幂等命中情况的结果对象:

1
2
3
4
5
public record ReplenishmentDraftResult(
String draftNo,
String status,
boolean reused) {
}

在前面的 SupplyChainMcpTools 中注入 ReplenishmentApplicationService,再增加写工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@McpTool(
name = "create_replenishment_draft",
description = "创建补货建议草稿。仅保存草稿,不审批、不生成采购订单。相同idempotencyKey重复调用不会重复创建。",
generateOutputSchema = true,
annotations = @McpTool.McpAnnotations(
readOnlyHint = false,
destructiveHint = false,
idempotentHint = true,
openWorldHint = false))
public ReplenishmentDraftResult createReplenishmentDraft(
@McpToolParam(description = "SKU编码", required = true)
String skuCode,
@McpToolParam(description = "目标仓库ID", required = true)
Long warehouseId,
@McpToolParam(description = "建议补货数量,必须大于0", required = true)
Integer quantity,
@McpToolParam(description = "本次操作的幂等键,建议使用UUID", required = true)
String idempotencyKey) {

if (quantity == null || quantity <= 0 || quantity > 100_000) {
throw new IllegalArgumentException("quantity必须在1到100000之间");
}

return replenishmentApplicationService.createDraft(
skuCode, warehouseId, quantity, idempotencyKey);
}

这个写工具需要同时满足几条工程约束:

  • 幂等键必须由服务端落库并建立唯一约束,不能只在内存里判断。
  • 创建人应来自认证身份或服务账号,不能信任模型传入的 operatorId
  • 草稿、审批、提交采购单要拆成不同工具,权限分别控制。
  • 每次调用记录 tool 名、参数摘要、调用身份、结果、耗时和 trace ID。
  • 返回结果要包含草稿编号和状态,但不要返回供应商银行卡、完整成本明细等无关敏感字段。

第六步:启动服务并让 Codex 连接

先在本地启动 Spring Boot 应用:

1
./mvnw spring-boot:run

然后在另一个终端把服务加入 Codex:

1
2
codex mcp add supply-chain-local --url http://127.0.0.1:8088/mcp
codex mcp list

也可以直接编辑全局的 ~/.codex/config.toml,或者在受信任项目中使用 .codex/config.toml

1
2
3
4
5
6
7
8
9
10
[mcp_servers.supply_chain_local]
url = "http://127.0.0.1:8088/mcp"
enabled = true
enabled_tools = [
"query_inventory_snapshot",
"create_replenishment_draft"
]
default_tools_approval_mode = "writes"
startup_timeout_sec = 10
tool_timeout_sec = 30

default_tools_approval_mode = "writes" 的含义是:只读工具可以顺畅使用,可能写数据的工具需要经过确认。还可以用 enabled_tools 做工具白名单,避免服务以后新增工具时自动扩大 Codex 的权限。

配置完成后重新启动 Codex 客户端。在 Codex CLI 中输入 /mcp,可以查看当前已连接的 MCP Server 和工具列表。ChatGPT 桌面端和 Codex IDE 扩展也可以在 MCP servers 设置中添加相同的 Streamable HTTP URL。

第七步:在 Codex 中验证真实调用

第一轮先只测试读工具,提示词可以这样写:

1
2
3
请使用 supply-chain-local MCP 查询 SKU-10086 在仓库 3101 的库存快照。
列出现存、锁定、可用和在途数量,并说明数据刷新时间。
本次只允许查询,不要创建补货草稿。

确认 Codex 调用了 query_inventory_snapshot,再测试一段完整业务流程:

1
2
3
4
请先查询 SKU-10086 在仓库 3101 的库存。
如果可用量低于 100,计算补到 500 所需的数量并解释计算过程。
先把建议展示给我,等我明确确认后,才能调用 create_replenishment_draft。
不要审批草稿,也不要创建采购订单。

这段提示词故意把“查询、分析、确认、写入”拆开。合理的执行顺序应该是:

  1. Codex 调用只读工具获得实时库存。
  2. Codex 根据数据计算建议补货量。
  3. 用户检查 SKU、仓库、数量和原因。
  4. 用户确认后,Codex 才调用写工具。
  5. Spring 服务校验权限和幂等键,创建草稿并写审计日志。

Spring项目升级为MCP服务的实施流程

第八步:生产环境认证与权限控制

本地开发可以绑定 127.0.0.1 并暂时不启用认证,但远程 MCP 服务绝不能裸奔在公网或办公网中。

Codex 的 Streamable HTTP MCP 配置支持从环境变量读取 Bearer Token:

1
2
3
4
5
6
[mcp_servers.supply_chain_prod]
url = "https://mcp.example.internal/mcp"
bearer_token_env_var = "SUPPLY_CHAIN_MCP_TOKEN"
enabled_tools = ["query_inventory_snapshot"]
default_tools_approval_mode = "writes"
tool_timeout_sec = 30

Token 应由系统凭据库、CI/CD Secret 或企业身份系统注入,不要写进 Git 仓库、AGENTS.md、提示词或博客示例配置。需要用户级权限时,应采用 OAuth;机器到机器的固定任务可以使用独立服务身份,但必须限制作用域和有效期。

生产环境至少要建立下面几层保护:

  • 网络层:优先部署在内网、VPN 或零信任访问边界后,只开放 HTTPS。
  • 身份层:验证 Bearer Token 或 OAuth 身份,不把模型提供的用户名当成真实身份。
  • 授权层:按工具和业务对象授权,例如只能查看所属组织的仓库。
  • 业务层:继续执行库存状态、单据状态、数量上限和审批规则。
  • 审计层:保留谁在什么时间调用了哪个工具、影响了什么业务对象。
  • 客户端层:Codex 配置工具白名单,并让写工具进入人工确认。

第九步:测试不能只看“工具能被发现”

一个 MCP Server 能在 /mcp 中列出工具,只说明协议接通了,不代表它可以安全上线。测试应该分四层。

1. 普通单元测试

直接测试 MCP 适配器的参数校验和返回字段,保证空 SKU、非法仓库和超大数量会被拒绝。

2. 业务服务测试

继续测试原来的库存口径、数据权限、事务和幂等性。特别要验证相同 idempotencyKey 调用两次,只产生一条补货草稿。

3. MCP 集成测试

启动完整 Spring 上下文,通过 MCP client 完成 initializetools/listtools/call。不要把 /mcp 当普通 REST 接口,只用浏览器或一条简单 curl 判断成功,因为 MCP 请求包含协议握手、JSON-RPC 消息和会话信息。

4. Codex 验收测试

至少准备一组固定问题,验证 Codex 是否选择了正确工具、参数是否正确、只读任务是否不会误调用写工具,以及服务异常时是否会停止而不是猜测结果。

推荐把下面这些检查加入发布清单:

1
2
3
4
5
6
7
8
9
[ ] codex mcp list 能看到服务
[ ] /mcp 能看到预期工具
[ ] 只读工具不会写数据库
[ ] 写工具需要人工确认
[ ] 非法参数返回可理解的错误
[ ] 幂等键重复调用不会重复写入
[ ] 超时、限流和下游异常有明确结果
[ ] 日志包含 traceId,但不记录 Token 和敏感字段
[ ] enabled_tools 只包含本次批准上线的工具

常见问题排查

现象 优先检查
Codex 连接时报 404 Codex URL 是否包含 /mcpmcp-endpoint 是否被 context-path 或网关改写
服务启动成功但没有工具 工具类是否在 Spring 扫描范围,是否使用新的 org.springframework.ai.mcp.annotation 包,annotation scanner 是否启用
返回 400 或 415 是否把 MCP 端点当作普通 REST API 调用,客户端协议和 STREAMABLE 配置是否一致
Codex 仍显示旧的工具描述 修改 schema 后重启 Spring 服务,并让 Codex 客户端重新连接
写工具重复产生数据 幂等键是否真正落库并建立唯一约束,事务边界是否覆盖检查和写入
调用经常超时 同时检查 Spring 的 request-timeout、Codex 的 tool_timeout_sec 和下游数据库/HTTP 超时
本地正常,经过网关后断流 检查反向代理对流式 HTTP、缓冲、空闲超时和长连接的配置
Boot 3 项目出现类或方法不存在 Spring Boot、Spring AI、MCP Java SDK 是否跨发布线混用

工具设计比接通协议更重要

把 Spring 项目改造成 MCP Server,技术上只需要增加 starter、配置端点、写几个注解方法。但真正决定它能不能长期使用的,是工具边界。

我会坚持几个原则:默认只读;写操作从草稿开始;一个工具只做一件业务动作;参数使用业务语言;输出字段最小化;服务端永远重新鉴权和校验;每次调用可审计、可追踪、可限流。

这样设计后,Codex 不需要知道库存表有多少张、订单状态分散在哪些服务里,也不需要持有数据库账号。它只需要知道“什么时候查询库存”和“什么时候创建补货草稿”。Spring 项目则继续守住真正重要的业务边界。

MCP 的价值不是让 AI 获得无限权限,而是把原来散落在数据库、接口和人工操作里的能力,收敛成一组清楚、最小、可验证的工具。对 Java 项目来说,这种升级方式既能复用成熟的 Spring 工程体系,也能让 Codex 真正参与到实时业务诊断和开发验证中。

参考资料

Pi从入门到精通:用极简Agent构建供应链智能助手

Pi 是 Mario Zechner 发起的开源 Agent Harness。它不是一个大模型,而是一套把大模型、上下文、工具、会话和终端界面组合起来的运行框架。它既提供可以直接使用的编程 Agent,也提供 SDK、RPC、Skills 和 Extensions,适合继续封装成企业内部的 AI 助手。

本文以 2026 年 8 月 1 日的官方版本为基准,从安装和第一次对话开始,逐步讲到项目规则、模型配置、技能、扩展工具、SDK、Java 系统集成、安全控制和效果评估。业务示例统一使用供应链系统中的订单、库存、采购和补货场景。

Pi 已迁移到 earendil-works/pi,npm 包也已改为 @earendil-works/*。旧的 badlogic/pi-mono@mariozechner/* 资料可能仍能搜索到,但新项目应使用当前名称。

Pi Agent Harness组件架构

一、先理解Pi解决什么问题

一个可执行的 AI Agent 至少需要五部分:

  1. 模型:负责理解目标、推理和决定下一步动作。
  2. 上下文:告诉模型项目结构、业务术语和工程约束。
  3. 工具:让模型读取文件、执行命令或调用业务接口。
  4. Agent 循环:重复执行“模型判断、调用工具、读取结果、继续判断”。
  5. 会话:保存消息、工具结果、模型状态和上下文压缩结果。

Pi 把这些能力拆成多个包:

职责
@earendil-works/pi-ai 统一不同模型提供商的调用接口
@earendil-works/pi-agent-core Agent 循环、工具调用和状态管理
@earendil-works/pi-coding-agent 可直接使用的 CLI、SDK、会话和扩展系统
@earendil-works/pi-tui 终端交互界面

因此,Pi 有四种常见用法:

  • 直接运行 pi,把它当作终端编程助手。
  • 运行 pi -p,完成一次性、非交互任务。
  • 运行 pi --mode rpc,通过 JSONL 接入 Java、Python 或桌面应用。
  • 在 Node.js/TypeScript 服务中使用 AgentSession SDK。

二、安装Pi并完成第一次运行

1. 检查Node.js版本

当前 pi-coding-agent 要求 Node.js >=22.19.0

1
2
node --version
npm --version

如果版本过低,先通过 Node.js 官方安装包、nvm 或 fnm 升级。

2. 全局安装

1
2
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
pi --version

--ignore-scripts 会阻止依赖安装阶段执行生命周期脚本,Pi 的正常 npm 安装不依赖这些脚本。

Windows PowerShell、macOS 和 Linux 都可以使用相同的 npm 安装命令。如果安装成功但提示找不到 pi,应检查 npm 全局可执行目录是否已加入 PATH

1
npm config get prefix

3. 登录模型提供商

进入任意项目目录后启动:

1
2
cd /path/to/supply-chain-system
pi

在 Pi 里执行:

1
/login

可以选择官方支持的订阅登录,也可以使用 API Key。以 PowerShell 为例,临时设置 OpenAI API Key:

1
2
$env:OPENAI_API_KEY="你的API Key"
pi

Linux 或 macOS:

1
2
export OPENAI_API_KEY="你的API Key"
pi

不要把 API Key 写入 Git 仓库、AGENTS.md、提示词或日志。也可以通过 /login 把凭证交给 Pi 管理,默认凭证文件位于 ~/.pi/agent/auth.json

4. 第一次供应链任务

先让 Pi 理解项目,不要一上来就让它改代码:

1
2
3
请先读取 README.md、pom.xml 和订单、库存、采购模块的目录结构。
说明创建采购订单后,库存预占、补货计算和供应商下单分别在哪些模块完成。
只分析,不修改文件,也不要执行写数据库的命令。

Pi 默认给模型提供 readwriteeditbash 四个工具。模型并不是直接操作电脑,而是在 Agent 循环中选择工具,Pi 执行工具后把结果返回给模型。

三、用AGENTS.md建立项目规则

AI 在供应链项目里最容易犯的错误,不是 Java 语法错误,而是误解库存口径、订单状态和事务边界。应在仓库根目录创建 AGENTS.md,把长期有效的规则放进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Supply Chain Project Instructions

## Modules
- order-service: 销售订单和状态流转
- inventory-service: 现存量、预占量、冻结量和库存流水
- procurement-service: 采购申请、采购订单和供应商协同
- replenishment-service: 安全库存与补货建议

## Business Rules
- 可用库存 = 现存量 - 预占量 - 冻结量。
- 库存扣减必须使用带数量条件的原子更新,并记录库存流水。
- 同一个 businessRequestId 重试时不得重复预占或释放库存。
- 采购订单状态只能按状态机流转,禁止直接覆盖状态。
- 跨服务一致性使用本地事务加 Outbox 事件,不创建分布式大事务。

## Safety
- 默认只允许查询本地开发库,不连接生产数据库。
- 未经明确确认,不执行 INSERT、UPDATE、DELETE、DDL 和消息重放。
- 不读取或输出 .env、密钥、Token、客户手机号和供应商银行信息。

## Verification
- Java 代码修改后运行 mvn -pl <module> test。
- 库存并发逻辑必须增加重复请求、库存不足和并发扣减测试。
- 数据库变更必须提供可回滚迁移脚本和索引影响说明。

Pi 启动时会加载全局 ~/.pi/agent/AGENTS.md,以及当前目录和父目录中的 AGENTS.mdCLAUDE.md。修改规则后执行 /reload,或者重新启动 Pi。

项目规则应该描述稳定事实,不要把某次临时需求全部塞进去。临时需求应放在单独的需求文档中,再通过 @文件名 引用。

四、掌握一套可靠的日常开发流程

以“订单创建时预占库存”为例,可以把一次任务拆成五个阶段。

阶段1:建立上下文

1
2
读取 @AGENTS.md、@docs/inventory-model.md,搜索现有的库存扣减、释放、流水和幂等实现。
列出相关文件、当前调用链、事务边界和测试入口。不要修改代码。

这一步要求 Pi 先找到项目里的真实模式,避免凭经验发明不存在的层次和组件。

阶段2:输出实施计划

1
2
3
4
5
6
7
8
9
10
11
需求:订单创建成功后预占库存;订单取消或支付超时后释放;重复消息不能重复处理。

请给出实施计划,必须包含:
1. 正常流程和异常流程。
2. 表字段、唯一键、版本号和条件更新SQL。
3. 本地事务与Outbox事件边界。
4. 幂等键定义和重复消费处理。
5. 需要修改的文件清单。
6. 单元测试、并发测试和集成测试用例。

只输出计划,等待确认后再编码。

阶段3:小步实现

确认计划后再执行:

1
2
按计划先完成库存预占的领域逻辑和仓储层,只修改 inventory-service。
不要修改公共依赖版本。完成后展示差异并运行该模块测试。

一次只改变一个业务闭环。订单、库存、采购三个服务同时大改,会让上下文迅速膨胀,也会增加错误定位成本。

阶段4:验证业务不变量

至少检查以下不变量:

  • 可用库存不能为负数。
  • 相同幂等键只能产生一次库存流水。
  • 释放数量不能超过原预占数量。
  • 数据库回滚时不能留下已发送但无法追踪的业务事件。
  • 消息重复、乱序、延迟时,最终状态仍然正确。

可以让 Pi 主动补测试:

1
根据本次 diff 检查库存不变量。补充库存不足、相同幂等键重复请求、20线程并发预占、事务回滚四组测试,然后运行测试并解释失败原因。

阶段5:审查和提交

1
2
审查当前 git diff,优先寻找数据一致性、并发、幂等、空值、越权和日志泄密问题。
没有阻塞问题后,给出建议的提交信息,但不要自动推送。

这个流程的重点是把“理解、计划、实现、验证、审查”分开。模型能力再强,也不应该跳过业务确认和可重复验证。

五、模型、思考强度与会话管理

切换模型

在交互模式中:

1
/model

也可以使用 Ctrl+L 打开模型选择器,使用 Shift+Tab 切换思考强度。简单的格式调整不需要高强度推理;跨服务库存一致性、死锁分析和数据迁移则适合更高强度。

项目级 .pi/settings.json 可以保存团队约定,例如:

1
2
3
4
5
6
7
8
9
{
"defaultProvider": "openai",
"defaultModel": "你的模型ID",
"defaultThinkingLevel": "medium",
"enabledModels": [
"openai/*",
"anthropic/*"
]
}

模型 ID 会随提供商更新,先用 /modelpi --list-models 查看当前可用值,不要照抄过时文章里的模型名。

管理长任务

1
2
3
pi -c                  # 继续最近会话
pi -r # 浏览历史会话
pi --name "库存预占改造"

交互模式还提供 /new/resume/tree/fork/clone。建议一个会话只解决一个明确问题。需求方向发生变化时,通过 /fork 保留原分支,比在同一上下文里反复推翻更清晰。

接入本地模型

Pi 可以通过 ~/.pi/agent/models.json 接入 Ollama、LM Studio、vLLM 等 OpenAI 兼容服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false
},
"models": [
{
"id": "你的本地模型ID",
"reasoning": false
}
]
}
}
}

本地模型适合代码检索、摘要和低风险分类,但是否能处理复杂库存一致性问题,必须通过真实测试集评估,不能只看通用排行榜。

六、用Skill沉淀供应链知识

AGENTS.md 是所有任务都要遵守的项目规则,Skill 则是按需加载的专业流程。可以创建:

1
.pi/skills/supply-chain-review/SKILL.md

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
name: supply-chain-review
description: 审查订单、库存、采购和补货代码。当任务涉及库存数量、状态机、幂等、消息或供应链数据一致性时使用。
---

# Supply Chain Review

1. 先识别业务实体、状态和数量口径。
2. 画出同步调用、事务提交和异步消息的先后顺序。
3. 检查幂等键来源、唯一约束和重复消息处理。
4. 检查库存更新是否包含 `available_qty >= request_qty` 条件。
5. 检查超时、取消、部分成功和补偿路径。
6. 检查日志是否泄露客户、供应商或凭证信息。
7. 输出按严重程度排序的问题,并给出文件位置和测试建议。

项目被信任后,Pi 会发现 .pi/skills/ 下的技能。重载后可以显式调用:

1
/skill:supply-chain-review 审查本次库存预占改动

Skill 可以附带 scripts/references/assets/。例如把库存字段字典、订单状态机和消息规范放进 references/,只有任务需要时才加载,能减少固定上下文占用。

七、用Extension接入只读库存接口

Skill 只是告诉模型怎么工作,Extension 才能注册真正可调用的工具。下面创建一个项目级只读库存查询工具:

1
.pi/extensions/inventory-query.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

export default function (pi: ExtensionAPI) {
pi.registerTool({
name: "query_inventory",
label: "Query Inventory",
description: "按SKU和仓库查询只读库存快照,不执行预占、扣减或释放",
parameters: Type.Object({
sku: Type.String({ description: "SKU编码" }),
warehouseCode: Type.String({ description: "仓库编码" }),
}),
async execute(_toolCallId, params, signal) {
const baseUrl = process.env.SCM_API_BASE_URL;
const token = process.env.SCM_API_TOKEN;

if (!baseUrl) {
throw new Error("SCM_API_BASE_URL is not configured");
}

const url = new URL("/api/v1/inventory/availability", baseUrl);
url.searchParams.set("sku", params.sku);
url.searchParams.set("warehouseCode", params.warehouseCode);

const headers: Record<string, string> = {
Accept: "application/json",
};
if (token) headers.Authorization = `Bearer ${token}`;

const response = await fetch(url, { headers, signal });
if (!response.ok) {
throw new Error(`Inventory API failed: HTTP ${response.status}`);
}

const snapshot = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(snapshot, null, 2),
},
],
details: {
sku: params.sku,
warehouseCode: params.warehouseCode,
status: response.status,
},
};
},
});
}

配置环境变量后启动 Pi:

1
2
3
$env:SCM_API_BASE_URL="https://scm-api.example.internal"
$env:SCM_API_TOKEN="短期只读Token"
pi

首次加载项目级 Extension 时,Pi 会要求确认是否信任项目。执行 /reload 后,可以输入:

1
查询 SKU-10001 在 WH-SZ-01 的库存快照,结合近30天日均销量和7天采购提前期判断是否需要补货。只生成建议,不创建采购单。

这个工具应使用专门的只读服务账号,并在 API 网关限制路径、仓库范围、频率和返回字段。不要为了方便给 Agent 一个可以调用任意内部接口的管理员 Token。

八、使用SDK构建补货建议服务

如果要把 Pi 嵌入 Node.js 服务,使用 SDK 比启动子进程更直接:

1
npm install @earendil-works/pi-coding-agent typebox

下面是一个最小的补货建议 Agent。真实项目应把 queryReplenishmentInputs 替换成经过鉴权的内部 API 客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import {
createAgentSession,
defineTool,
ModelRuntime,
SessionManager,
} from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

const scmApiBaseUrl = process.env.SCM_API_BASE_URL;
const scmApiToken = process.env.SCM_API_TOKEN;
if (!scmApiBaseUrl || !scmApiToken) {
throw new Error("SCM_API_BASE_URL and SCM_API_TOKEN must be configured");
}

const replenishmentInputs = defineTool({
name: "get_replenishment_inputs",
label: "Get Replenishment Inputs",
description: "读取SKU补货计算所需的库存、销量、在途和提前期数据",
parameters: Type.Object({
sku: Type.String(),
warehouseCode: Type.String(),
}),
async execute(_toolCallId, params, signal) {
const query = new URLSearchParams({
sku: params.sku,
warehouseCode: params.warehouseCode,
});
const response = await fetch(
`${scmApiBaseUrl}/api/v1/replenishment/inputs?${query}`,
{
headers: {
Authorization: `Bearer ${scmApiToken}`,
},
signal,
},
);
if (!response.ok) throw new Error(`SCM API returned ${response.status}`);
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data) }],
details: { sku: params.sku, warehouseCode: params.warehouseCode },
};
},
});

const modelRuntime = await ModelRuntime.create();
const { session } = await createAgentSession({
modelRuntime,
sessionManager: SessionManager.inMemory(process.cwd()),
tools: ["get_replenishment_inputs"],
customTools: [replenishmentInputs],
});

session.subscribe((event) => {
if (
event.type === "message_update" &&
event.assistantMessageEvent.type === "text_delta"
) {
process.stdout.write(event.assistantMessageEvent.delta);
}
});

await session.prompt(`
分析 SKU-10001 在 WH-SZ-01 的缺货风险。
先调用工具获取事实,再按以下公式给出建议:
补货点 = 提前期日均需求 + 安全库存。
输出事实、计算过程、风险、建议数量和需要人工确认的假设。
禁止创建采购单。
`);

这段代码只开放了一个自定义工具,没有给 Agent bashwriteedit。服务端 Agent 应按场景建立工具白名单,不要照搬编程助手的默认权限。

九、Java系统通过RPC接入Pi

供应链后台通常是 Java 技术栈。此时可以单独部署一个 Pi Agent Sidecar,由 Java 服务通过 RPC 调用:

1
pi --mode rpc --no-session

RPC 使用标准输入和标准输出传输 JSONL,每行一个 JSON 对象。Java 服务发送:

1
{"id":"replenishment-10001","type":"prompt","message":"分析SKU-10001在WH-SZ-01的缺货风险,只输出补货建议"}

Pi 会先返回请求是否被接受,再持续输出 message_updatetool_execution_starttool_execution_endagent_end 等事件。

生产环境建议采用以下结构:

  1. Java 业务服务负责登录态、租户、权限和参数校验。
  2. Node.js Agent 服务或 Pi RPC Sidecar 负责会话和模型编排。
  3. Agent 只能通过受控工具访问订单、库存和采购 API。
  4. 工具层统一做超时、重试、熔断、脱敏和审计。
  5. 采购单创建等写操作进入待审批队列,由业务服务执行。

不要为每个请求临时启动一个 Pi 进程。可以维护固定数量的 Sidecar,按租户和任务隔离会话;如果主系统本身可以部署 Node.js 服务,优先使用 SDK,类型和生命周期管理会更简单。

十、供应链补货Agent的完整技术方案

Pi供应链补货Agent执行流程

一个可上线的补货 Agent 可以分成四层。

1. 入口层

  • Web、企业微信或 ERP 页面接收用户问题。
  • Java 网关校验用户、租户、仓库和数据权限。
  • 把自然语言转换为带 requestId 的 Agent 请求。

2. Agent编排层

  • Pi AgentSession 保存一次分析任务的上下文。
  • 系统提示词限定角色、公式、输出格式和禁止事项。
  • Skill 提供补货分析步骤和业务术语。
  • 模型决定先查询哪些事实,再形成结论。

3. 工具层

至少拆成这些只读工具:

  • get_inventory_snapshot:现存、预占、冻结、可用和在途库存。
  • get_sales_forecast:历史销量、预测量和促销修正。
  • get_supplier_lead_time:供应商交期、最小起订量和包装倍数。
  • get_open_purchase_orders:未关闭采购单和预计到货日期。
  • calculate_replenishment:使用确定性代码计算补货点和建议量。

计算公式应该由确定性工具执行,而不是让模型心算。例如:

1
2
3
4
有效库存位置 = 可用库存 + 在途库存 - 已分配未出库数量
补货点 = 采购提前期内预测需求 + 安全库存
建议补货量 = max(0, 目标库存 - 有效库存位置)
最终采购量 = 按最小起订量和包装倍数向上取整

模型负责解释数据、识别异常和组织建议,确定性程序负责算数、状态校验和写入。

4. 审批与执行层

Agent 的结果先形成 ReplenishmentProposal

1
2
3
4
5
6
7
8
9
{
"requestId": "RP-20260801-0001",
"sku": "SKU-10001",
"warehouseCode": "WH-SZ-01",
"suggestedQty": 240,
"reasonCodes": ["BELOW_REORDER_POINT", "LEAD_TIME_RISK"],
"evidenceVersion": "inventory-88921",
"status": "PENDING_APPROVAL"
}

审批通过后,由采购服务重新读取库存版本、校验建议是否过期,再创建采购申请。不要让模型直接拼接 SQL 写数据库,也不要把“模型已经判断正确”当作越过业务校验的理由。

十一、生产安全与权限控制

Pi 默认没有内置沙箱。bash、Extension 和第三方 Package 都以 Pi 进程的系统权限运行。项目信任机制只能控制是否加载项目资源,不等于运行时安全边界。

生产环境至少落实以下措施:

  1. 进程隔离:在容器、虚拟机或受限系统账号中运行 Agent。
  2. 工具白名单:业务 Agent 不开放任意 bash、文件写入和通用 HTTP 工具。
  3. 最小权限:库存工具只读,且限制租户、仓库、接口和字段。
  4. 人工审批:采购下单、库存调整、价格修改必须经过确认。
  5. 参数校验:服务端重新校验 SKU、数量、状态和数据版本。
  6. 凭证隔离:Token 存在密钥系统或环境变量中,不进入模型上下文。
  7. 防提示词注入:把接口返回的数据当作不可信数据,不执行其中夹带的指令。
  8. 审计留痕:记录用户、模型、会话、工具、脱敏参数、耗时、结果版本和审批人。
  9. 预算控制:限制单次任务轮数、上下文、Token、超时和并发数。
  10. 降级策略:模型或工具不可用时回退到传统规则引擎和人工流程。

第三方 Skill、Extension 和 Pi Package 可能包含可执行代码,安装前必须审查源码并固定版本。

十二、评估Agent是否真的可用

不要用“回答看起来不错”作为上线标准。可以从历史供应链数据中构造一套脱敏测试集:

  • 正常补货 SKU。
  • 促销导致需求突增的 SKU。
  • 有大量在途库存的 SKU。
  • 供应商延迟交付的 SKU。
  • 库存数据缺失或时间戳过期的 SKU。
  • 多仓调拨比采购更合适的 SKU。
  • 最小起订量和包装倍数冲突的 SKU。

关键指标包括:

指标 说明
工具选择准确率 是否调用了正确的事实查询和计算工具
参数准确率 SKU、仓库、租户和时间范围是否正确
事实一致率 输出数字是否与工具结果一致
建议接受率 采购人员接受或小幅调整建议的比例
危险动作率 未审批写入、越权访问等事件,目标必须为0
任务成功率 在超时和重试限制内完成的比例
P95耗时与成本 评估交互体验和规模化成本

每次升级模型、提示词、Skill 或 Extension,都应重放同一批测试集并比较结果。只有回归指标稳定,才能扩大业务范围。

十三、常见问题排查

pi命令不存在

检查 Node.js 版本、全局安装结果和 npm prefix:

1
2
3
node --version
npm list -g @earendil-works/pi-coding-agent
npm config get prefix

登录成功但找不到模型

先执行 pi --list-models,检查提供商凭证和 models.json。自定义无密钥本地服务也需要占位 apiKey,否则模型可能不会出现在可用列表中。

项目Skill或Extension没有加载

确认目录分别是 .pi/skills/.pi/extensions/,确认已经信任当前项目,然后执行 /reload。非交互模式不会显示信任对话框,需要预先保存信任决策,或者在明确理解风险时使用一次性的 --approve

Agent修改范围越来越大

重新开会话,把任务缩小到一个模块;在提示词中列出允许修改的目录;先让 Pi 输出文件清单和计划,再允许编辑。

结果包含正确解释但数字算错

把公式和取整规则实现成确定性工具。模型只能传入参数、调用工具和解释结果,不能作为财务、库存或采购数量的唯一计算器。

十四、推荐的进阶路线

  1. 第一天:完成安装、登录、文件引用、模型切换和会话恢复。
  2. 第二天:为现有 Java 供应链项目编写 AGENTS.md
  3. 第三天:用 Pi 完成一个低风险代码审查和测试补充任务。
  4. 第四天:创建供应链审查 Skill,沉淀团队检查清单。
  5. 第五天:编写一个只读库存查询 Extension。
  6. 第六天:使用 SDK 构建补货建议原型,并建立离线测试集。
  7. 第七天以后:接入审批、审计、监控、限流和容器隔离,再考虑试点上线。

总结

Pi 的价值不在于预置了多少复杂流程,而在于它提供了一套小而清晰的 Agent 基础能力:统一模型接口、Agent 循环、会话、工具、TUI、Skills、Extensions、SDK 和 RPC。初学时可以直接使用 CLI,提高 Java 项目的检索、修改和测试效率;进阶后可以通过 Skill 固化供应链知识,通过 Extension 接入内部只读 API;真正上线时,则应使用 SDK 或 RPC,把 Pi 放在权限、审批和审计体系之内。

供应链 Agent 最可靠的职责是“查询事实、执行确定性计算、解释风险、生成建议”,而不是绕过业务服务直接修改库存和采购数据。把模型的推理能力与传统系统的规则、事务和权限结合起来,才是从能演示走向能生产的关键。

参考资料

Loop Engineering实战:从日志巡检到供应链异常修复闭环

从一次性修复到持续治理

最近在整理 Codex 和 Agent 工作流时,我越来越觉得,AI 编程真正值得关注的地方,已经不只是“怎么写一个更好的提示词”,而是怎么把 AI agent 放进一个持续运转的工程循环里:自动发现问题、定位根因、生成修复、跑测试、部署预发,再由独立验证环节判断是否真的修好。

换句话说,AI 编程的瓶颈,已经不只是“写代码慢不慢”,而是“发现问题、修复问题、验证问题、沉淀经验”这条维护链路是不是还靠人手动推动。

如果人每天都要打开日志平台、复制错误、问 AI、改代码、跑测试、提合并、盯预发,那 AI 只是提高了某个环节的速度。Loop Engineering 要做的,是把整条链路设计成能自己转起来的闭环。

Loop Engineering供应链异常修复闭环

工程循环的五个必要环节

Loop Engineering 的重点不是让 AI “更会写代码”,而是把问题发现、任务隔离、修复执行、独立验证和经验沉淀组织成可重复运行的工程流程。自动化程度必须由风险等级决定,尤其不能默认把生产数据修改和生产发布交给 agent。

我把它总结成四个观点。

第一,维护循环才是真正瓶颈。

AI 已经能很快生成代码,但线上系统的问题不会自动消失。日志分散、错误类型多、排查链路长、测试和部署还要人工推动,才是工程效率真正卡住的地方。

第二,Loop 比一次性 Agent 多了“持续性”。

普通 Harness 是一次会话里给 AI 工具,比如 shell、git、日志查询、测试命令。Loop 在它之上增加了调度、状态、独立验证和跨轮记忆。也就是说,它不是“这次帮我修一下”,而是“每天自己巡检、自己修、自己验,必要时通知人”。

第三,Loop 需要五个动作。

1
2
3
4
5
发现:找出该处理的问题。
交付:把问题隔离给 agent 执行。
验证:让独立检查环节判断结果。
持久化:把结论、失败和修复经验记录下来。
调度:让这套流程定时或按事件反复运行。

第四,Loop 需要六类组件。

1
2
3
4
5
6
Connectors:连接日志、监控、发布、通知等系统。
Automations:定时巡检或事件触发。
Skills:把诊断、修复、发布流程写成 SOP。
Worktrees:隔离不同修复任务,避免互相污染。
Independent verifier:用独立上下文或独立检查程序验证,避免修复者自证。
State:在工单、数据库或版本化文档中记录历史结论、修复方案和巡检状态。

这里还有一个很重要的判断标准:不是所有任务都值得建 Loop。适合建 Loop 的任务,通常要满足四个条件:重复发生、验证能自动化、成本可控、agent 有足够工具。

放到供应链系统里怎么理解

供应链系统非常适合用 Loop Engineering 的思路,因为它的异常不是一次性的。

比如这些问题会反复出现:

1
2
3
4
5
6
7
订单履约超时。
采购到货数量和采购单不一致。
库存预占失败。
出库单状态卡住。
消息消费失败。
供应商回传状态延迟。
报表统计口径和业务事实不一致。

传统做法是:运营或研发发现异常,去查订单、查库存流水、查 MQ、查日志、查数据库,再手动判断是否要修代码或补数据。

Loop Engineering 的做法是把这条链路设计成系统:

1
2
3
4
5
6
7
8
9
每天自动扫描异常订单和错误日志。
按业务类型归类:库存、采购、发货、消息、报表。
让 Codex 读取相关代码、日志和数据样本。
生成根因报告和最小修复方案。
补测试或验证 SQL。
修复一个小切片。
跑测试和回归查询。
通过后提交变更,失败则进入调试循环。
超过重试次数就停止,并把问题交给人。

这样 Codex 不只是“写代码的人”,而是整个维护循环里的执行者。

Codex 供应链案例

假设供应链系统里有一个反复出现的问题:采购到货后,部分订单的库存流水已经写入,但采购到货单状态仍停留在“部分到货”,导致后续财务暂估和供应商履约报表都不准。

这个需求不要直接让 Codex 改代码。先把 loop 定义清楚。

第一步,用普通任务说明定义目标。不同 Codex 版本和使用界面支持的能力并不完全相同,因此不要假设 /goal 是所有环境都可用的通用命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
请把下面内容作为本次持续排查任务的目标:
持续排查并修复供应链系统里的采购到货状态异常问题。

目标:
1. 找出近 7 天库存流水已写入但到货单状态未正确更新的样本;
2. 定位是代码逻辑、消息消费、事务回滚还是历史数据导致;
3. 如果是代码问题,生成最小修复并补充测试;
4. 跑相关测试和验证 SQL;
5. 输出修复报告、风险点和人工验收清单。

停止条件:
- 找到明确根因并完成修复;或
- 连续两轮验证失败;或
- 需要真实数据库权限、生产凭证、业务口径确认。

第二步,让 Codex 先做发现。

1
2
3
4
5
6
7
8
9
10
11
请先不要改代码。
请读取项目规则、采购到货模块、库存流水模块、消息消费模块和相关测试。

同时整理需要验证的数据条件:
1. purchase_receipt 状态不是 COMPLETED;
2. stock_ledger 已存在对应 receipt_id;
3. receipt_item 的 received_qty 已等于 planned_qty;
4. 最近 7 天内出现;
5. 排除人工关闭或取消的单据。

输出异常样本查询 SQL、可能原因列表和下一步排查计划。

第三步,把诊断流程沉淀成 skill 思路。

1
2
3
4
5
6
请按 supply-chain-receipt-diagnosis skill 的方式执行:
1. 先确认业务状态机;
2. 再查库存流水和到货明细;
3. 再查消息消费日志;
4. 再看最近相关 git diff;
5. 最后输出事实、推理、结论,不要跳步。

如果还没有这个 skill,可以先把它当成提示词使用。等这类问题出现三次以上,就值得写成真正的 SKILL.md

第四步,让 Codex 生成修复计划。

1
2
3
4
5
6
7
8
请基于上面的诊断结果,生成修复计划,不要直接改代码。

计划必须包含:
1. 要修改的 Service、Mapper、消息消费者或定时补偿任务;
2. 要新增的单元测试和集成测试;
3. 是否需要补偿历史数据;
4. 事务边界和幂等风险;
5. 回滚方案。

第五步,小步实现。

1
2
3
4
5
6
7
8
9
现在只实现第一步:补充一个失败测试。

测试场景:
库存流水写入成功,到货明细数量已满足完成条件,但到货单状态仍未更新。

要求:
- 先让测试失败;
- 不修改业务实现;
- 完成后展示测试名称和失败原因。

第六步,再让 Codex 做最小修复。

1
2
3
4
5
6
7
8
现在根据失败测试做最小修复。

要求:
- 不改接口协议;
- 不重构无关代码;
- 保持现有事务模型;
- 如果涉及消息重复消费,必须加幂等判断;
- 修复后运行相关测试。

第七步,独立验证。

1
2
3
4
5
6
7
8
请用 code review 的方式审查当前 diff。

重点检查:
1. 是否只是修复采购到货状态异常;
2. 是否会影响少到、多到、不合格品逻辑;
3. 是否有重复消费导致状态反复更新的问题;
4. 库存流水和到货状态是否在同一事务或有补偿机制;
5. 测试是否覆盖重复消息和事务失败。

第八步,持久化经验。

1
2
3
4
5
6
7
8
请把这次问题总结成一条可复用的排查规则:
- 触发条件;
- 相关表;
- 相关日志;
- 常见根因;
- 验证 SQL;
- 修复注意事项;
- 下次自动巡检建议。

这一步形成可审计的状态记录。记录应放在团队可维护的工单、runbook 或版本库中,不能只依赖某次对话的上下文。

完整闭环与质量门禁

对供应链系统来说,一个可落地的 Codex Loop 可以这样走:

1
2
3
4
5
6
7
8
9
10
11
定时巡检
-> 查询异常订单/到货单/库存流水
-> Codex 读取代码和日志
-> 生成根因报告
-> 写失败测试
-> 最小修复
-> 跑测试和验证 SQL
-> 独立 review
-> 预发验证
-> 人工确认生产发布
-> 记录经验和新规则

关键不是追求“全自动发布生产”,而是按风险设置质量门禁。涉及库存数量、金额、权限和生产数据修复时,应要求人工审批、双人复核、审计记录和可执行的回滚方案。

落地限制与治理措施

第一个限制,是验证器不够强。

如果只有单元测试,AI 可能把错误隐藏掉,比如把错误日志降级,或者只处理 happy path。解决办法是至少三层验证:单元测试、业务 SQL 校验、预发环境回归。

第二个限制,是成本容易失控。

每天全量扫日志、读代码、跑大模型诊断,很容易消耗大量 token。解决办法是分级:先用简单规则和小模型筛选高风险问题,再把少量问题交给 Codex 深度处理。

第三个限制,是工具链建设成本高。

Loop 不是只写提示词,它需要日志查询、测试命令、发布脚本、通知通道、状态记录。解决办法是先选一个窄场景,比如“采购到货状态异常”,不要一上来覆盖整个供应链。

第四个限制,是自动修复可能误伤。

供应链系统里状态机、库存、财务、报表互相影响,自动修复不能绕过人。解决办法是设置硬停止条件:涉及金额、库存数量、生产数据修复、权限变更时,必须人工审批。

第五个限制,是经验没有沉淀。

如果每次排查都停留在一次对话里,下一次还是从零开始。解决办法是把常见问题写入 skill、runbook、异常规则库或巡检配置,让 loop 越跑越聪明。

总结

Loop Engineering 最值得借鉴的地方,是它把 AI 编程从一次性任务推进到生产闭环。它不是让 AI 一次性写更多代码,而是设计一套能持续发现问题、修复问题、验证问题、沉淀经验的系统。

放到供应链系统里,我认为最适合先做的不是“让 Codex 自动开发大需求”,而是“让 Codex 自动巡检和修复高频异常”。比如采购到货状态异常、库存流水不一致、消息消费失败、订单履约超时。

真正稳的 AI 工程化,不是把人完全拿掉,而是让人从手动推动循环,变成设计循环、审查风险和批准关键节点。

参考资料

CC Switch管理Codex多套配置:认证、供应商与本地路由

为什么需要配置管理

最近使用 Codex 做代码任务时,我越来越明显地感觉到,AI 编程工具本身已经不是唯一问题,真正容易乱的是账号、模型、API、MCP 和本地配置。

比如一台电脑上可能同时有个人 ChatGPT 账号、公司授权账号、测试账号;有时候想用官方 Codex 登录状态,有时候又想切到第三方 OpenAI 兼容 API;再加上 ~/.codex/auth.json~/.codex/config.toml、MCP 配置和项目里的 AGENTS.md,时间一长就很容易不知道当前 Codex 到底在用哪个账号、哪个模型、哪个接口。

cc-switch 要解决的就是这个问题。它不是模型,也不是 Codex 的替代品,而是一个 AI 编程工具的配置管理面板。我的理解是:Codex 负责干活,cc-switch 负责把账号、供应商、模型和路由管理清楚。

cc-switch管理多个GPT账号给Codex使用

适用场景与合规边界

先说结论:cc-switch 适合管理多套 AI 编程配置,但不要把它理解成“无限切账号绕过限制”的工具。

我觉得它最适合三类场景。

第一类,是个人账号和公司账号分开。比如个人项目用自己的 ChatGPT / Codex 账号,公司项目用公司授权账号,避免上下文、账单和权限混在一起。

第二类,是官方登录和第三方 API 分开。Codex App 或 Codex CLI 可能需要官方登录状态,但实际模型请求可以根据任务切到不同 provider,比如 OpenAI 官方、OpenAI 兼容网关、团队内部代理或其他模型服务。

第三类,是不同任务使用不同模型。轻量任务用便宜模型,复杂重构用更强模型,长上下文分析用支持更大上下文的 provider。

这里要注意一个边界:多个账号必须是你自己合法拥有或团队授权使用的账号,不要把多账号当成规避平台限额的手段。auth.json、API Key、refresh token 都属于敏感凭证,不要截图、不要提交到 Git、不要发给别人。

先理解 Codex 的两个关键配置

Codex 本地一般会涉及两个重要文件:

1
2
~/.codex/auth.json
~/.codex/config.toml

auth.json 更像登录态,保存官方 ChatGPT / Codex OAuth 登录缓存。它很敏感,不应该手工复制、上传或分享。

config.toml 是 Codex 的运行配置文件,可包含模型、模型供应商、接口地址和相关选项。认证信息如何保存取决于登录方式、Codex 版本以及 CC Switch 的配置模式,不应把某一种文件布局当成长期稳定的公开接口。

CC Switch 在管理 Codex 时,主要价值是减少手工修改配置文件的次数。尤其是在切换不同 provider 时,它会按当前版本支持的方式更新 Codex 配置。操作前仍应备份配置,并在升级后核对 CC Switch 的发布说明。

如果你不了解这两个文件,就很容易出现一个错觉:Codex 界面显示的是 A 账号,所以请求一定走 A 账号。实际不一定。官方登录状态、模型请求路由、账单来源可能是三件事,需要分别确认。

安装与变更前准备

Windows 上可以直接去 GitHub Releases 下载 .msi 安装包,或者使用 portable zip。

macOS 可以使用 Homebrew:

1
2
brew tap farion1231/ccswitch
brew install --cask cc-switch

Linux 可以下载 .deb 或 AppImage。服务器上如果没有桌面环境,也可以使用 Web 版本,默认端口是 17666

安装后,先不要急着添加一堆账号。建议先做三件事:

1
2
3
1. 确认 Codex CLI 或 Codex App 可以正常启动;
2. 确认 cc-switch 能看到 Codex 这个应用入口;
3. 备份当前 ~/.codex 目录,避免误操作后不好恢复。

备份可以这样做:

1
cp -r ~/.codex ~/.codex.backup.$(date +%Y%m%d)

Windows PowerShell 可以用:

1
Copy-Item "$env:USERPROFILE\.codex" "$env:USERPROFILE\.codex.backup.20260705" -Recurse

方式一:保留官方登录并切换Provider

这是需要同时保留官方登录能力和第三方 provider 时的一种用法。官方认证保留在当前 CC Switch 版本中是可选设置,默认值和写入方式可能随版本变化,应以当前发布说明为准。

第一步,在 cc-switch 的 Codex 面板里选择 OpenAI Official。如果没有,就从 preset 里添加一个。

第二步,启动 Codex,完成一次官方 ChatGPT / Codex 登录。登录后,Codex 会把登录缓存写到 ~/.codex/auth.json

第三步,回到 cc-switch,打开:

1
Settings -> General -> Codex App Enhancements

打开类似这样的选项:

1
Keep official login when switching third-party providers

这个开关的意思是:切换第三方 provider 时,尽量保留官方登录缓存,不要反复覆盖 auth.json。这样 Codex 仍然能识别官方账号,而模型请求可以走当前选中的 provider。

第四步,在 Codex 面板里新增 provider。比如你可以添加一个 OpenAI 兼容 API、团队代理网关,或者其他支持 Responses API / Chat Completions 的模型服务。

第五步,切换 provider 后重启 Codex。这个动作很重要,因为 Codex 通常在启动时读取 config.toml 和模型列表。你切换 provider 后不重启,可能还在用旧配置。

验证时不要只看 Codex 显示的账号。应该同时看三处:

1
2
3
1. cc-switch 当前启用的 Codex provider;
2. cc-switch routing / request log 是否有请求;
3. provider 后台余额或调用记录是否发生变化。

如果 Codex 仍显示官方账号,但 provider 后台出现调用记录,这是正常的:官方账号负责登录态,实际模型请求走当前 provider。

方式二:通过OAuth Auth Center隔离授权账号

CC Switch 的 OAuth Auth Center 可以管理多个经过本人或组织授权的 ChatGPT / Codex OAuth 账号。该能力仍带有版本和合规风险,适合做身份、权限和账单隔离,不应被用于共享凭证或规避平台限制。

需要区分两件事:在 Codex 中切换本人获授权的登录身份,与把 Codex OAuth 服务反向代理给其他工具并不是同一种操作。后者可能受到 OpenAI 与上游服务条款限制。启用任何 OAuth reverse proxy 或第三方转发功能前,应阅读当前版本的风险提示和相关服务条款;公司环境还需要经过安全与合规审批。

大致步骤是:

1
2
3
4
5
6
7
8
1. 打开 Settings -> OAuth Auth Center;
2. 在 ChatGPT / Codex OAuth 区域点击登录;
3. 按提示复制设备验证码;
4. 打开授权地址并登录第一个 ChatGPT 账号;
5. 授权成功后,账号会出现在 Logged-in Accounts;
6. 点击 Add Another Account,再登录第二个账号;
7. 每个 provider 选择对应账号保存;
8. 通过 provider 卡片或托盘菜单切换。

这里要特别小心:不要导出 token,也不要手工复制 refresh token。一个更好的习惯是只让 cc-switch 自己维护 OAuth 状态。账号过期就重新登录,不要把凭证文件拿来传来传去。

如果是多人共用一台开发机,更建议每个人使用自己的系统用户,或者至少明确命名 provider:

1
2
3
codex-personal-gpt
codex-company-gpt
codex-test-gpt

名字要能看出用途,不要只叫 account1account2。半年后再看,自己也会忘。

方式三:协议不兼容时使用Local Routing

部分第三方 provider 只提供 Chat Completions 兼容协议,而 Codex 使用的工具调用和流式事件更接近 Responses API。直接修改接口地址可能出现模型目录、流式响应或工具调用不兼容,此时可评估使用 CC Switch Local Routing 做协议转换。

不同版本的菜单名称可能变化,当前版本可从 Routing 相关设置进入:

1
Settings -> Routing -> Local Routing

打开主开关后,默认本地服务一般是:

1
127.0.0.1:15721

然后只勾选 Codex 的 routing takeover。这样 Codex 请求会先到本地 cc-switch 路由,再由 cc-switch 转发给真正的 provider。

这个模式有两个好处。

第一,真实 API Key 不一定直接暴露在 Codex 当前配置里,可以由 cc-switch 的 provider 配置管理。

第二,可以处理协议不完全一致的问题,比如把 Codex 的请求转换成上游 provider 能接受的格式。

但本地路由也会多一层故障点。如果 Codex 返回 404、模型不存在、流式响应异常,要优先检查:

1
2
3
4
5
1. provider 是否需要 Local Routing;
2. Local Routing 主开关是否启动;
3. Codex routing takeover 是否打开;
4. model mapping 是否写对;
5. Codex 是否已经重启。

本地路由位于代码、提示词和模型服务之间,也扩大了敏感数据的处理边界。使用前应确认日志是否记录请求正文、凭证如何存储、上游是否保留数据,以及团队代码是否允许发送给该 provider。

日常使用与审计流程

我会把日常流程固定成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
开始任务前:
1. 打开 cc-switch;
2. 确认当前 Codex provider;
3. 确认账号用途:个人、公司还是测试;
4. 确认模型和路由状态;
5. 再启动 Codex。

任务执行中:
1. 不在对话里粘贴 API Key;
2. 不让 Codex 输出 auth.json;
3. 大任务先让 Codex 计划,再执行;
4. 涉及敏感代码或生产数据时缩小上下文。

任务结束后:
1. 看 provider 调用记录;
2. 看本地 git diff;
3. 如果切过账号,回到默认 provider;
4. 必要时清理临时日志和敏感文件。

这个流程看上去啰嗦,但能避免很多麻烦。AI 工具越自动化,越需要把“当前是谁在用、用哪个模型、请求去哪儿了”这件事弄清楚。

常见故障与安全风险

第一个坑,是把多账号当成额度池。这个风险很高,也不稳定。账号切换应该服务于权限隔离、账单隔离和项目隔离,而不是绕限制。

第二个坑,是手动复制 auth.json。这个文件里可能包含敏感登录缓存,复制来复制去很容易泄露,也容易因为凭证轮换导致失效。

第三个坑,是切换 provider 后不重启 Codex。很多配置是在启动时加载的,特别是模型列表和 config.toml

第四个坑,是只看 Codex 界面上的账号显示。实际请求走哪里,要看 cc-switch 当前 provider、routing log 和 provider 侧账单记录。

第五个坑,是 provider 名字乱。建议用用途命名,比如 personal-openai-officialcompany-openai-gatewaytest-codex-oauth

总结

cc-switch 对 Codex 最大的价值,不是“多一个工具”,而是让账号、模型、路由和配置变得可控。

如果只是偶尔用 Codex,一个官方登录就够了。但如果你同时维护个人项目、公司项目、测试环境,还要在官方账号和不同 provider 之间切换,那么 cc-switch 可以明显降低配置混乱。

我的建议是:先从一个官方账号加一个 provider 开始,跑通登录、切换、重启、验证这条链路;等流程稳定后,再添加第二个 GPT 账号。不要一开始就把所有账号和模型都塞进去,越复杂越容易排查困难。

真正好用的 AI 编程环境,不是账号越多越好,而是每一次启动 Codex 前,都清楚当前账号是谁、请求会去哪、出了问题该看哪里。

参考资料

CodeGraph与Ponytail组合实战:让Agent找得准、改得少

CodeGraph 和 Ponytail 经常一起出现在 Agent 工具列表中,但两者不是同一类插件:CodeGraph 解决“代码在哪里、如何调用、改动影响哪里”,Ponytail 解决“理解之后,最少需要构建什么”。单独使用任何一个都不完整。

只使用 CodeGraph,Agent 可能准确找到十个相关文件,却顺手设计新的接口、工厂和扩展框架;只使用 Ponytail,Agent 可能写出一个很小的补丁,却因为没有发现另一个调用入口而遗漏真实根因。组合后的正确顺序是先扩大理解范围,再缩小实现范围。

本文从 Codex 的安装配置开始,设计一套适用于 Java 供应链项目的完整工作流,并以“部分出库订单取消后正确释放剩余预占库存”为例,演示结构查询、影响分析、最小方案、实现、索引同步、双重审查和测试选择。

CodeGraph与Ponytail供应链Agent闭环

一、先明确两个工具的职责

工具 输入 输出 不负责什么
CodeGraph 本地项目源码和结构查询 相关源码、调用路径、符号关系、影响范围 不决定需求是否合理,不证明代码正确
Ponytail 已理解的需求、代码和工程约束 最小正确实现策略、过度设计审查 不自动建立完整代码图,不替代安全审查

组合后的职责链:

1
2
3
4
5
6
7
CodeGraph:找到真实业务流和共享边界
-> 人与Agent:确认业务不变量和需求范围
-> Ponytail:选择最高层可复用方案
-> Codex:实施最小改动
-> CodeGraph:重新检查影响和测试范围
-> Ponytail Review:删除人为增加的复杂度
-> 普通Review和测试:验证正确性、安全与数据一致性

这里最重要的是顺序。Ponytail 官方规则也要求先读懂问题并追踪真实流程,不能为了追求小 diff 而跳过理解。CodeGraph 正好为这一步提供预构建的项目关系上下文。

二、组合方案的技术架构

一套推荐的开发机架构包括:

1
2
3
4
5
6
7
8
Codex CLI或桌面版
├─ CodeGraph MCP
│ └─ .codegraph/codegraph.db(本地SQLite索引)
├─ Ponytail插件
│ ├─ Session/Prompt/Subagent生命周期Hook
│ └─ ponytail、review、audit、debt等Skills
├─ Git工作区
└─ Maven/Gradle、JUnit、Testcontainers和数据库迁移工具

CodeGraph 的索引数据库留在项目 .codegraph/ 中,其内部 .gitignore 会忽略生成的索引文件;不要把 SQLite、锁和日志提交到仓库。可以提交的是项目级 codegraph.json,因为它定义了团队共同的 include、exclude 和扩展名映射。

Ponytail 的模式通常是个人会话状态,项目不应依赖某位开发者恰好启用了 full。真正不可违反的供应链规则必须写入版本控制内的 AGENTS.md,并由测试和数据库约束执行。

三、一次完成安装与验证

1. 安装CodeGraph

已有 Node.js 时:

1
2
3
npm install -g @colbymchenry/codegraph
codegraph version
codegraph install --target=codex --location=global --yes

为供应链项目初始化索引:

1
2
3
cd D:\projects\supply-chain-platform
codegraph init
codegraph status

2. 安装Ponytail

1
2
codex plugin marketplace add DietrichGebert/ponytail
codex plugin add ponytail@ponytail

启动 Codex,进入 /hooks,审阅并信任 Ponytail 的生命周期 Hook。然后重启 Codex 或桌面应用并新建任务。

3. 验证两个插件

在终端先验证图查询:

1
codegraph explore "库存预占的创建、发货扣减和取消释放如何连接"

在 Codex 中验证 Ponytail:

1
2
@ponytail-help
@ponytail full

再执行组合探针:

1
2
使用 CodeGraph 找到库存释放的全部调用者,并按 Ponytail full 判断项目中是否已有可复用的幂等处理。
只输出分析结果,不修改代码。

如果 Agent 没有调用 codegraph_explore,检查项目是否有有效 .codegraph/ 索引以及 Codex 是否在安装 CodeGraph 后重启。如果 Ponytail 没有报告当前模式,检查插件、Node.js 和 Hook 信任状态。

四、统一项目规则

供应链项目根目录的 AGENTS.md 可以加入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# AI Development Workflow

## Understand Before Editing
- 回答代码结构、业务流和影响范围问题时,优先使用CodeGraph。
- 修改公共方法、DTO、事件或Mapper前,检查调用者和影响范围。
- 图中缺失的消息Topic、反射、动态SQL和外部系统关系必须读取配置或测试确认。
- CodeGraph提示索引待同步时,读取最新文件或执行状态检查。

## Minimal Implementation
- 理解完整流程后执行Ponytail决策阶梯。
- 优先复用项目现有领域服务、事务模板、幂等组件和测试工具。
- 禁止没有真实第二个实现的接口、工厂和策略注册中心。
- 禁止为推测需求增加配置、表字段、消息Topic和新依赖。
- 每次只修改完成当前业务闭环所必需的文件。

## Supply Chain Invariants
- 可释放量 = 预占量 - 已出库量 - 已释放量,结果不得小于0。
- 同一消费方和事件ID只能产生一次业务副作用。
- 库存余额、库存流水、幂等记录和Outbox必须遵守已定义的事务边界。
- 租户、仓库、SKU、批次和货主数据权限不可删除。
- 金额和数量口径必须保持精度、单位与舍入规则。

## Verification
- Ponytail Review不能替代正确性、安全和性能Review。
- 核心库存改动至少验证部分出库、重复事件、并发和事务回滚。
- CodeGraph affected只用于选择附加测试,固定核心回归集始终运行。

如果 CodeGraph 安装器已经写入带标记的说明区块,不要在整理 AGENTS.md 时删除标记。团队规则放在单独标题下,避免升级或卸载时与工具管理的区块混在一起。

五、完整案例与需求边界

业务问题:订单预占 10 件商品,仓库已出库 4 件。此时用户取消剩余部分,系统应释放 6 件预占库存。由于消息系统至少一次投递,相同取消事件可能重复到达,但只能产生一次释放流水。

已知约束:

  • 已出库 4 件不能回到可售库存。
  • 释放量不能超过剩余预占量。
  • 重复事件不能产生第二条流水。
  • 库存余额、流水和幂等记录必须原子提交。
  • 正常整单取消、支付超时和人工取消仍要保持兼容。
  • 本次不建设通用库存策略平台。

这个需求很适合组合使用两个工具:CodeGraph 需要先找出全部入口和共享计算位置,Ponytail 再阻止 Agent 为一个公式差异创建一套策略体系。

六、阶段1:建立干净基线

开始任务前执行:

1
2
3
git status --short
codegraph status
mvn -pl inventory-service -am test

目的分别是:

  1. 确认不会覆盖用户已有改动。
  2. 确认图索引可用且没有待同步文件。
  3. 确认目标模块在改动前是绿的。

如果工作区已有无关修改,记录并避开;不要为了让 Agent 获得“干净环境”而重置他人的工作。

七、阶段2:让CodeGraph追踪真实业务流

第一条提示只做结构分析:

1
2
3
4
5
6
7
8
9
10
11
12
使用 CodeGraph 分析“订单取消后释放库存”的完整调用链,只分析,不修改代码。

请返回:
1. Kafka或应用入口;
2. 订单ID、事件ID、SKU和数量如何传递;
3. 可释放数量在哪个方法计算;
4. 库存余额、预占记录、库存流水和幂等记录分别在哪里写入;
5. 本地事务边界;
6. 正常取消、支付超时、人工取消和售后入口;
7. 现有单元测试和集成测试。

对配置、反射、动态SQL或跨服务关系明确标注“图外待确认”。

查询应尽量包含真实业务动作,而不是只问“库存模块怎么工作”。拿到候选符号后,继续收窄:

1
2
使用 CodeGraph 详细分析 InventoryReservation.releasableQty 及全部调用者。
返回该方法源码、调用路径和修改它的影响范围。

也可以人工运行:

1
2
codegraph callers InventoryReservation.releasableQty
codegraph impact InventoryReservation.releasableQty --depth 4

假设结果显示:

1
2
3
4
5
6
7
OrderCancelledConsumer
PaymentTimeoutConsumer
ManualOrderCloseService
-> InventoryReleaseApplicationService.release
-> InventoryReservation.releasableQty
-> InventoryReservationRepository.save
-> InventoryLedgerRepository.append

这说明修复应围绕共享领域方法评估,而不是只修改最初报错的 OrderCancelledConsumer

八、阶段3:补齐图外事实

CodeGraph 是静态代码图,异步 Topic、MyBatis 动态 SQL 和 Spring 运行时代理可能需要额外确认。让 Agent 读取:

1
2
3
4
5
6
7
8
基于CodeGraph结果,再读取以下图外证据:
- OrderCancelled Topic与Consumer Group配置;
- InventoryReservationMapper.xml中的条件更新SQL;
- 事务注解和传播级别;
- processed_event唯一索引迁移;
- 重复投递集成测试。

将“源码图确认”“配置确认”“仍是推测”分成三组输出。

供应链系统的关键关系经常由字符串连接:例如生产者发送 order.cancelled.v2,消费者监听 ${topic.order-cancelled}。图中没有直接调用边,不代表业务链不存在。

完成后整理一张事实表:

事实 证据 状态
所有取消入口最终调用同一 release 方法 CodeGraph调用路径 已确认
可释放量未扣除 shippedQty 领域方法源码 已确认
幂等记录与库存写入同一事务 事务代码和集成测试 已确认
外部WMS可能晚到回传 接口文档,当前仓库无实现 待业务确认

只有已确认事实才能作为实现依据;未确认项要么继续取证,要么明确排除在本次需求之外。

九、阶段4:运行Ponytail决策阶梯

现在才进入最小方案阶段:

1
2
3
4
5
6
7
8
9
10
11
12
@ponytail full

基于刚才确认的调用链和事实表,给出最小正确方案。
逐项回答:
1. 是否需要新增能力;
2. 项目里已有哪个共享边界可以修复;
3. 是否已有幂等组件和条件更新模式;
4. 是否需要新接口、工厂、策略、配置、表或依赖;
5. 最少修改哪些文件;
6. 必须新增哪些测试。

不要编码,先输出方案和被拒绝的过度设计选项。

合理的决策记录可能是:

1
2
3
4
5
6
需求需要实现:部分出库取消是现有业务分支。
项目已有共享边界:InventoryReservation.releasableQty。
项目已有幂等组件:ProcessedEventExecutor,无需新建Redis锁。
数据库已有version条件更新,无需新锁框架。
最小改动:修正一个领域公式,补充两个测试;必要时调整已有SQL条件。
跳过:ReleaseStrategy接口、CancelTypeFactory、新配置项、新依赖和新表。

Ponytail 不应该为了“少改文件”拒绝必要的数据库约束或集成测试。如果调查发现现有 SQL 没有原子条件,新增迁移或 Repository 修改就是当前需求的真实复杂度,不能省略。

十、阶段5:实施最小正确修改

假设现有领域方法错误地写成:

1
2
3
public int releasableQty() {
return reservedQty - releasedQty;
}

最小根因修复是:

1
2
3
public int releasableQty() {
return Math.max(0, reservedQty - shippedQty - releasedQty);
}

但单看这一行还不能提交。必须检查数量类型、状态约束和并发更新方式。如果数量允许小数,应该使用项目现有的 BigDecimal 或数量值对象,而不是照抄 int 示例。

Repository 的条件更新可以保持原子约束:

1
2
3
4
5
6
7
UPDATE inventory_reservation
SET released_qty = released_qty + #{releaseQty},
version = version + 1,
updated_at = CURRENT_TIMESTAMP
WHERE id = #{id}
AND version = #{version}
AND reserved_qty - shipped_qty - released_qty >= #{releaseQty}

应用服务校验更新行数:

1
2
3
4
5
6
7
8
int updated = reservationRepository.release(
reservation.id(),
releaseQty,
reservation.version()
);
if (updated != 1) {
throw new ConcurrentInventoryChangeException(reservation.id());
}

如果这些 SQL 和异常处理已经存在,本次只需要改公式和测试,不能为了文章示例重复创建。Agent 的实现提示应明确:

1
2
3
4
按确认方案实施,只修改共享可释放量计算和对应测试。
复用现有幂等执行器、Repository条件更新和异常类型。
不要创建新接口、工厂、配置、新表或新依赖。
完成后展示diff并运行inventory-service测试。

十一、阶段6:同步图并复查影响

保存文件后,CodeGraph 会监听变化并增量同步。先检查:

1
codegraph status

如果运行环境禁用了自动同步,执行:

1
codegraph sync

然后要求 Agent 基于最新图复查:

1
2
使用 CodeGraph 基于最新索引重新分析 InventoryReservation.releasableQty。
确认所有调用者仍走共享实现,列出本次改动的影响范围和可能遗漏的测试。

这里不是机械地重复第一次查询。第一次是决定改哪里,第二次是确认实际 diff 是否符合计划,以及是否因改名、移动或新增调用产生了新的影响。

十二、阶段7:先做Ponytail Review

1
@ponytail-review

可以增加供应链上下文:

1
2
3
@ponytail-review
审查当前diff中的过度设计,只列出可删除或复用项。
不要把并发条件、事务、幂等、库存流水、权限和必要测试当作冗余。

预期它会发现类似问题:

1
2
InventoryReleaseStrategy.java: yagni: 只有一个实现,直接保留现有领域方法。
CancelInventoryProperties.java: delete: 没有任何环境修改该开关,本需求不需要配置。

如果当前 diff 已经只有公式和测试,正确结果可以是 Lean already. Ship.。这只表示没有明显过度设计,不表示正确性已经验证。

十三、阶段8:再做普通正确性审查

执行独立提示:

1
2
3
4
5
6
7
8
9
10
11
按正常代码审查标准检查当前diff。
优先查找:
1. 部分出库数量计算错误;
2. 重复消息与幂等事务边界;
3. 并发更新丢失;
4. 释放量为负或超过剩余预占;
5. 租户、仓库和货主越权;
6. 库存余额与流水不一致;
7. 测试缺口。

使用CodeGraph确认问题涉及的调用者,但以实际源码、SQL和测试为证据。

Ponytail Review 明确不负责正确性、安全和性能。这两轮 Review 不能合并成一句“帮我审查一下”,否则 Agent 可能只选择其中一个视角。

十四、阶段9:选择并运行测试

先运行固定目标测试:

1
2
mvn -pl inventory-service -Dtest=InventoryReservationTest test
mvn -pl inventory-service -Dtest=OrderCancellationInventoryIT test

核心用例至少包括:

1
2
3
4
5
6
预占10,出库0,释放10
预占10,出库4,释放6
预占10,出库10,释放0
同一事件重复两次,只产生一条释放流水
两个线程并发释放,最终释放量不超过可释放量
库存写入异常,幂等记录和流水全部回滚

再用 CodeGraph 查找附加回归测试:

1
2
git diff --name-only origin/master...HEAD |
codegraph affected --stdin --filter "**/*Test.java" --quiet

根据输出运行关联模块:

1
mvn -pl inventory-service,order-service -am test

affected 只能根据代码依赖提供候选测试。消息 Topic、外部 WMS 和数据库触发器可能不在静态依赖图中,因此固定的库存核心回归集不能被它替换。

十五、可复用的组合提示模板

1. 缺陷定位

1
2
3
4
5
使用 CodeGraph 追踪【现象】涉及的完整调用链和全部调用者。
将代码图证据、配置证据和推测分开。
确认根因位置后,使用 Ponytail full 设计最小正确修复。
复用项目现有模式,不新增推测性抽象。
实施后同步索引、复查影响、运行Ponytail Review、普通Review和相关测试。

2. 新增功能

1
2
3
4
需求:【业务需求】。
先用 CodeGraph 找出相似功能、公共入口、持久化边界和测试模式。
再按 Ponytail 阶梯判断能否复用项目、标准库、平台或现有依赖。
先给计划,不编码。计划必须列出业务不变量、最小文件清单、明确不做的内容和验证命令。

3. 安全重构

1
2
3
4
使用 CodeGraph 分析【符号】的调用者、被调用者和影响范围。
目标是减少重复,不改变外部行为。
使用 Ponytail full 选择删除或内联方案,禁止顺便引入新框架。
重构前后运行同一测试集,并比较公共API和数据库行为。

4. PR审查

1
2
3
4
先用 CodeGraph 根据diff检查公共符号影响和遗漏调用者。
再运行 Ponytail Review 寻找过度设计。
最后独立检查正确性、安全、并发、事务、幂等和测试缺口。
按严重程度输出发现,每条给文件和行号。

十六、在CI中使用CodeGraph,Ponytail留在开发环节

CodeGraph 的 CLI 适合在 CI 中辅助选择测试,但每次 CI 是否重建索引要考虑缓存、平台和执行时间。一个简单流程是:

1
2
3
4
5
6
检出代码
-> 恢复与当前OS匹配的CodeGraph缓存
-> codegraph status或init
-> codegraph sync
-> codegraph affected选择附加测试
-> 运行固定核心测试 + 附加测试

示意 PowerShell:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ErrorActionPreference = "Stop"

if (-not (Test-Path ".codegraph\codegraph.db")) {
codegraph init
} else {
codegraph sync
}

$changed = git diff --name-only "$env:BASE_SHA...$env:GITHUB_SHA"
$tests = $changed | codegraph affected --stdin --filter "**/*Test.java" --quiet
$tests | Set-Content affected-tests.txt

mvn -pl inventory-service -am test

不要在 Windows 与 Linux Runner 间复用同一 SQLite 索引缓存。索引及锁与操作系统相关,缓存键至少包含 OS、CodeGraph 版本和源码提交。

Ponytail 更适合开发和审查阶段,因为它通过 Agent 指令影响实现选择。CI 不应该靠 Ponytail 判断是否可以发布;CI 应使用可重复的编译、测试、静态检查、数据库迁移验证和安全扫描。

十七、组合使用的反模式

1. 让Ponytail先决定只改一个文件

还没查调用链就限定一个文件,可能把正确方案排除。应该先用 CodeGraph 扩大认知,再用 Ponytail 缩小实际改动。

2. CodeGraph返回十个文件就全部修改

相关文件不等于必须修改的文件。图结果用于理解和影响评估,Ponytail 决策阶梯用于判断哪些文件真正需要变化。

3. 把图中没有边当作没有业务关系

消息 Topic、反射、动态 SQL 和外部系统调用可能在图外。必须结合配置、日志和测试。

4. 用最少行数代替最小风险

删除唯一键、事务、幂等和测试会减少行数,但增加数据风险。最小正确实现以行为和风险为边界,不以 LOC 排名。

5. 只做Ponytail Review

它只检查过度设计,不检查正确性、安全和性能。必须再做普通 Review。

6. 每次查询都问整个系统

CodeGraph 的 explore 会返回密集源码上下文。长会话中反复询问大范围架构会占据上下文。应该以一个业务流或一个核心符号为单位查询,需求方向改变时开新任务。

7. 自动执行所有建议

CodeGraph 和 Ponytail 都是辅助工具。删除公共接口、修改库存 SQL、调整事务传播和执行数据库迁移必须经过人工业务与工程审查。

十八、安全与隐私

组合使用时要检查两种扩展权限:

  • CodeGraph MCP 能读取项目索引和返回源码片段。
  • Ponytail Hook 能在会话生命周期中注入规则。

安全建议:

  1. 只从官方仓库安装,安装前审阅脚本、插件清单和 Hook。
  2. 固定并分批升级版本,不在核心仓库自动追随未知更新。
  3. CodeGraph 索引本地运行,但返回给云模型的源码仍受模型数据策略约束。
  4. 不索引密钥、生产数据导出、客户隐私和供应商银行信息。
  5. Agent 默认不连接生产数据库,不执行写操作和消息重放。
  6. 需要时运行 codegraph telemetry off,并核对组织遥测政策。
  7. Ponytail 不能覆盖权限、审计和安全规则。

十九、常见冲突与排查

CodeGraph已安装,但Agent仍使用普通搜索

检查:

1
2
codegraph status
codegraph install --print-config codex

重启 Codex,并确认项目中存在有效索引。提示里明确要求“使用 CodeGraph”,以验证接入是否正常。

Ponytail让Agent过早缩小范围

把任务拆成两个阶段:第一阶段只允许 CodeGraph 分析,第二阶段才启用 @ponytail full 设计实现。也可以临时使用 lite

CodeGraph结果与最新源码不一致

1
2
codegraph status
codegraph sync

如果工具返回待同步提示,直接读取该文件最新内容,不要引用旧片段继续修改。

Ponytail报告接口多余,但它是外部系统边界

保留接口,并在 AGENTS.md 或架构决策记录中写明边界目的。单实现数量不是唯一判断标准,WMS、ERP 和承运商 Adapter 可能需要稳定隔离外部契约。

子Agent没有遵守工具规则

CodeGraph 安装器会把 CLI 指引写入 Agent 说明,以覆盖看不到 MCP 初始化指令的子 Agent;Ponytail 的 Hook 也会向子 Agent 注入模式。仍然不稳定时,减少子 Agent 使用,或在任务提示中显式重复“先 CodeGraph、后 Ponytail”的顺序。

二十、衡量组合方案是否有效

建议记录四组指标。

理解效率

  • 开始编码前的工具调用数。
  • 找到正确公共边界所需时间。
  • Review 阶段发现的遗漏调用者数量。

改动复杂度

  • 每个需求修改文件数和有效代码行。
  • 新增依赖、接口、工厂和配置项数量。
  • Ponytail Review 可删除行数。

交付质量

  • 编译和测试一次通过率。
  • 回滚、热修和线上数据修复次数。
  • 库存、金额、状态机和幂等相关缺陷。

Agent成本

  • 单任务 Token 和工具调用数。
  • 会话压缩次数。
  • 因上下文不清重复读取文件的次数。

目标不是把所有数字都降到最低。好的组合方案应该让 Agent 更早找到正确位置、提交更小的改动,同时保持或提高测试与生产质量。

二十一、推荐的进阶路线

  1. 入门:安装两个工具,在一个服务上完成结构查询和 Ponytail Review。
  2. 熟练:把“图查询、事实表、最小方案、双重 Review”固化成提示模板。
  3. 进阶:为公共符号改动建立 CodeGraph 影响分析检查,并按模块选择测试。
  4. 团队化:统一 AGENTS.mdcodegraph.json、核心回归集和升级策略。
  5. 度量化:以遗漏调用者、改动范围和生产缺陷评估,而不是只看 Token 或代码行。

总结

CodeGraph 与 Ponytail 组合的核心不是“装两个插件”,而是建立正确的工程顺序:先用代码图找到真实调用链和影响范围,再用最小实现规则消除不必要的抽象,最后用测试和普通审查验证业务正确性。

在供应链系统中,可以把这套方法概括为:理解要宽,改动要窄,验证要深。CodeGraph 让理解更有证据,Ponytail 让实现更克制,而库存不变量、事务、幂等、权限和测试决定最终结果能否上线。

参考资料