GEMINI.md 语法实战:5个真实项目案例教你打造高效AI助手
GEMINI.md 语法实战:5个真实项目案例教你打造高效AI助手
你是否曾有过这样的体验:向AI助手描述一个复杂的项目需求,得到的回复却总是隔靴搔痒,要么过于通用,要么偏离了你的技术栈和架构偏好?对于中高级开发者而言,通用AI模型的“万金油”式回答,在真实的、复杂的项目面前,往往显得力不从心。问题的核心在于,AI缺乏对项目专属语境的深度理解。它不知道你的团队为何选择TypeScript而非JavaScript,不清楚你的微服务边界如何划分,更不明白你代码库中那些约定俗成的“潜规则”。
这正是GEMINI.md存在的意义。它不是一个简单的配置文件,而是一份为AI量身定制的“项目入职手册”和“编码规范指南”。通过这份文档,你可以系统性地向AI灌输项目的灵魂——从业务目标、技术选型到代码风格和行为准则。今天,我们不谈空洞的理论,我将带你深入五个截然不同的真实项目场景,从电商秒杀到医疗数据分析,手把手演示如何运用GEMINI.md语法,将AI从一个“聪明的实习生”训练成与你并肩作战的“资深架构师”。你会发现,当AI真正理解了你的项目DNA,它的产出将精准得令人惊喜。
1. 高并发电商秒杀系统:定义性能与安全的铁律
电商秒杀场景是检验系统架构的试金石,它要求极致的性能、坚如磐石的数据一致性以及严密的安全防护。一个通用的AI助手可能会给出使用Redis缓存的建议,但我们需要的是能精确到毫秒级延迟、支持库存防超卖、并能抵御恶意请求的定制化方案。
1.1 构建坚不可摧的项目上下文
在GEMINI.md的开篇,我们必须用最清晰的语言为AI勾勒出战场全景。这不仅仅是技术栈列表,更是决策背后的深层逻辑。
项目目标与核心挑战: 我们的目标是构建一个能支撑瞬时百万级请求的秒杀系统。核心业务价值在于,在极短时间内(如1秒内)完成数万件热门商品的抢购,同时保证绝对公平(先到先得,杜绝机器人)和绝对准确(库存不超卖,订单不丢失)。主要的挑战包括:
- 瞬时流量洪峰:常规系统架构无法承受。
- 库存竞争:高并发下的数据一致性问题。
- 防刷与安全:识别并拦截黄牛脚本和恶意请求。
- 系统韧性:任何单点故障都可能导致灾难性后果。
技术栈与选型理由: 这里需要详细说明“为什么”,这能引导AI在后续建议中遵循同样的技术哲学。
- 后端语言:Go。选型理由是其卓越的并发原语(goroutine, channel)和接近C的性能,特别适合高并发、低延迟的IO密集型场景。
- 缓存与库存扣减:Redis + Lua脚本。选型理由是Redis的高性能内存读写,结合Lua脚本的原子性执行,可以确保库存查询和扣减在单次原子操作中完成,是解决超卖问题的关键技术。
- 消息队列:Apache Kafka。选型理由是其高吞吐、持久化和分区能力,用于将秒杀成功的请求异步化,削峰填谷,确保核心下单流程快速返回,后续订单处理由消费者服务异步完成。
- 数据库:MySQL(InnoDB引擎)。选型理由是事务支持(用于最终订单落库)和成熟的生态。配合读写分离架构,写主库,读从库。
- 网关与限流:Nginx + Lua (OpenResty)。选型理由是利用Nginx的高性能,在接入层实现基于IP、用户ID的限流和恶意请求过滤。
1.2 设定不容妥协的行为准则
在这一部分,我们要为AI塑造一个具有“秒杀系统架构师”思维的角色。
AI角色定义:你是一位专注于高并发、分布式系统与云原生架构的资深后端工程师。你的思维模式始终围绕性能、一致性、可用性、安全性(PCAS)四大原则展开。在给出任何方案时,必须优先评估其对系统吞吐量、延迟和稳定性的影响。
具体规则示例:
- 关于库存扣减:所有涉及库存扣减的逻辑,必须优先推荐使用 Redis Lua脚本 实现原子操作。禁止提出“先查询,后判断,再更新”的非原子化方案。
- 关于响应设计:所有核心秒杀接口(如
/seckill/submit)的响应时间必须低于50毫秒。接口响应体应极度精简,仅包含必要状态(如success,code,requestId),商品详情等富信息通过异步消息或后续查询接口获取。 - 关于限流与降级:当被问及如何保护系统时,必须提及多层次防御:接入层(Nginx)限流、服务层(Sentinel/Hystrix)熔断降级、以及业务层的队列缓冲。必须提供具体的配置片段或代码示例。
1.3 通过关键示例进行“肌肉记忆”训练
对于秒杀系统,我们需要用示例固化最关键的模式。以下是一个“用户提交秒杀请求”的Few-shot Example:
用户提问: “帮我实现秒杀请求提交的核心处理函数,需要检查库存并生成一个排队号。”
期望的AI回答:
// services/seckill_service.go
package services
import (
"context"
"github.com/go-redis/redis/v8"
"strconv"
)
type SeckillService struct {
rdb *redis.Client
}
// SubmitSeckillRequest 处理秒杀提交
func (s *SeckillService) SubmitSeckillRequest(ctx context.Context, userId int64, itemId string) (string, error) {
// 1. 参数校验 (略)
// 2. 使用Lua脚本原子化扣减库存并获取资格
luaScript := `
local stockKey = KEYS[1] -- 库存键,如 `seckill:stock:`..itemId
local userQueueKey = KEYS[2] -- 用户排队集合键
local userId = ARGV[1]
local stock = tonumber(redis.call('GET', stockKey))
if not stock or stock <= 0 then
return 0 -- 库存不足
end
-- 原子操作:库存减1,并将用户加入排队集合
redis.call('DECR', stockKey)
redis.call('SADD', userQueueKey, userId)
return 1 -- 成功
`
stockKey := "seckill:stock:" + itemId
userQueueKey := "seckill:queue:" + itemId
result, err := s.rdb.Eval(ctx, luaScript, []string{stockKey, userQueueKey}, userId).Int()
if err != nil {
return "", err
}
if result == 0 {
return "", errors.New("seckill item sold out")
}
// 3. 生成唯一的排队号(例如,结合时间戳、用户ID和随机数)
queueNumber := generateQueueNumber(userId)
// 4. 发送异步消息到Kafka,由下游服务处理订单创建、支付等
// sendToKafka("seckill-success-topic", message)
return queueNumber, nil
}
// generateQueueNumber 生成排队号示例
func generateQueueNumber(userId int64) string {
// 实际项目可能使用雪花算法等分布式ID生成器
return strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.FormatInt(userId, 10)
}
关键点提示:这个示例向AI强调了几个核心:原子操作是生命线、响应要快(操作主要在Redis内存中完成)、成功后的复杂逻辑要异步化。AI在后续遇到类似问题时,会优先采用这种模式。
2. 金融数据实时分析平台:确保准确性与可审计性
金融领域对数据的准确性、处理时效和操作可追溯性有着近乎苛刻的要求。这里的GEMINI.md需要引导AI成为一个严谨、细致且极度重视数据一致性的“金融系统专家”。
2.1 上下文:定义不可撼动的数据原则
项目核心目标:构建一个处理实时交易流水、进行风险指标计算和生成监管报表的平台。首要原则是数据100%准确,任何计算错误或数据丢失都是不可接受的。所有数据变更必须有完整的审计日志。
技术栈与选型深意:
- 流处理框架:Apache Flink。选型理由是其精确一次(Exactly-Once) 的状态一致性保证,这对于金融计算至关重要。同时,其事件时间(Event Time)处理和状态后端(State Backend)能力适合复杂的时间窗口计算。
- 数据存储:时序数据库 InfluxDB 用于存储指标数据,关系型数据库 PostgreSQL 用于存储维度数据和最终结果,两者结合。选型理由是InfluxDB对时间序列数据的高效压缩和查询优化,PostgreSQL满足复杂的关联查询和事务需求。
- 消息中间件:Apache Pulsar。选型理由是其原生的分层存储、多租户支持和强一致性,适合金融级的数据管道。
- 开发语言:Java (Flink生态) 和 Python (用于数据分析脚本)。选型理由是稳定性和丰富的金融计算库(如Pandas, NumPy)。
2.2 行为准则:植入金融级开发规范
AI角色定义:你是一位具有金融行业背景的数据平台架构师。你对数字极其敏感,坚信“垃圾进,垃圾出”。你的代码必须包含完善的错误处理、数据校验和监控埋点。任何可能产生歧义或近似计算的地方,都必须明确标出并给出误差范围说明。
强制性规则列表:
- 数据完整性:所有从消息队列消费数据的入口函数,必须实现幂等性处理逻辑,防止因重试导致数据重复计算。
- 计算可追溯:所有关键指标的计算公式,必须在代码中以注释或配置文件的方式明确写出,并注明数据来源和计算周期。
- 审计日志:任何对核心数据(如账户余额、交易状态)的修改操作,必须在数据库事务提交前,向专用的审计日志表(或发送审计事件)写入一条不可篡改的记录,包含操作人、时间、旧值、新值和变更原因。
- 数值处理:涉及货币计算,必须使用
BigDecimal(Java)或decimal.Decimal(Python)等精确数据类型,严禁使用float或double进行直接计算。
2.3 示例:定义一个风险指标计算任务
让我们通过一个“计算实时交易波动率”的例子来示教AI。
用户提问: “需要实现一个Flink作业,实时计算某个交易对在过去5分钟内的滚动波动率(标准差),数据源来自Kafka。”
期望的AI回答:
// RiskVolatilityJob.java
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;
// ... 其他导入
public class RiskVolatilityJob {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 启用Checkpoint,这是实现Exactly-Once语义的基础
env.enableCheckpointing(60000); // 每分钟一次Checkpoint
// 1. 定义数据源(假设Trade对象包含price和eventTime)
DataStream<Trade> tradeStream = env.addSource(new FlinkKafkaConsumer<>("trade-topic", new TradeDeserializer(), properties))
.assignTimestampsAndWatermarks(WatermarkStrategy.<Trade>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner((event, timestamp) -> event.getEventTime()));
// 2. 关键计算:滚动窗口波动率
DataStream<VolatilityAlert> volatilityStream = tradeStream
.keyBy(Trade::getSymbol) // 按交易对分组
.window(TumblingEventTimeWindows.of(Time.minutes(5))) // 5分钟滚动窗口
.process(new ProcessWindowFunction<Trade, VolatilityAlert, String, TimeWindow>() {
@Override
public void process(String key, Context context, Iterable<Trade> trades, Collector<VolatilityAlert> out) {
List<Double> prices = new ArrayList<>();
for (Trade t : trades) {
prices.add(t.getPrice());
}
// 使用Apache Commons Math或自定义函数计算标准差
double stdDev = calculateStandardDeviation(prices);
double mean = calculateMean(prices);
double volatility = stdDev / mean; // 相对波动率
// 3. 审计与输出:记录窗口时间、交易数量、计算结果
if (volatility > THRESHOLD) {
out.collect(new VolatilityAlert(key, context.window().getStart(), context.window().getEnd(),
prices.size(), volatility, System.currentTimeMillis()));
}
// 同时可以将结果写入InfluxDB用于监控
}
});
volatilityStream.addSink(new InfluxDBSink());
volatilityStream.print(); // 用于调试
env.execute("Real-Time Trade Volatility Calculation");
}
// 使用BigDecimal进行精确计算
private static double calculateStandardDeviation(List<Double> values) {
// ... 实现细节,注意使用高精度计算
}
}
注意:这个示例不仅给出了代码框架,更重要的是传递了金融数据处理的关键模式:事件时间、精确一次语义、窗口计算、审计输出。AI在后续处理类似流计算任务时,会自然地引入这些元素。
3. 跨平台移动应用开发:统一代码风格与组件契约
当团队使用React Native或Flutter开发跨平台应用时,最大的挑战之一是如何保持iOS和Android两端体验一致,以及如何管理日益庞大的组件库。GEMINI.md在此处的使命,是让AI成为团队代码规范的“守护者”和“活文档”。
3.1 上下文:确立多端一致的体验标准
项目目标:使用React Native开发一款面向全球用户的社交应用,要求iOS和Android双端在UI/UX上保持高度一致,性能流畅,并具备良好的可访问性(A11y)。代码库需要良好的可维护性,以支持数十名开发者的协同工作。
技术栈与设计系统:
- 框架:React Native 0.72+。选型理由是一次编写多端运行,拥有活跃的社区和丰富的第三方库。
- 状态管理:Zustand + React Query。选型理由是Zustand的简洁API适合管理客户端状态,React Query完美处理服务器状态(数据获取、缓存、同步)。
- UI组件库:自定义组件库基于React Native Paper + 自研主题封装。选型理由是实现设计统一和快速构建。
- 导航:React Navigation。选型理由是社区标准,功能全面。
- 类型系统:TypeScript。选型理由是在大型项目中保障类型安全,减少运行时错误。
3.2 行为准则:定义组件开发的“宪法”
AI角色定义:你是一位注重细节、对用户体验有极致追求的前端技术专家。你深谙React Native的最佳实践,并致力于编写声明式、可复用、可测试的组件。你时刻考虑性能(如列表优化)和可访问性。
核心开发规则:
- 组件设计:所有通用UI组件必须使用
React.forwardRef暴露Ref,以便父组件进行必要的控制。组件属性(Props)必须使用TypeScript接口明确定义,并为其提供详细的JSDoc注释。 - 样式管理:禁止在组件内部使用内联样式(
style={{}})。所有样式必须通过StyleSheet.create创建,或引用自全局主题(Theme)文件。颜色、字体、间距等必须使用主题变量(如theme.colors.primary)。 - 列表性能:当渲染可能很长的列表时,必须优先使用
FlatList或SectionList,并正确实现keyExtractor和getItemLayout(如果可行)以优化性能。 - 可访问性:所有可交互元素(按钮、链接)必须包含
accessible和accessibilityLabel属性。图片必须提供accessibilityHint。
3.3 示例:创建一个标准的按钮组件
通过一个按钮组件的创建示例,我们可以固化团队的UI开发规范。
用户提问: “我们需要一个通用的PrimaryButton组件,支持不同尺寸、加载状态,并且要符合我们的设计主题。”
期望的AI回答:
// src/components/Buttons/PrimaryButton.tsx
import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator, StyleSheet, ViewStyle, TextStyle } from 'react-native';
import { useTheme } from '../../theme/ThemeContext';
/**
* 主按钮组件,用于应用中最主要的操作。
* @param {Object} props - 组件属性
* @param {string} props.title - 按钮显示的文本
* @param {() => void} props.onPress - 点击回调函数
* @param {'small' | 'medium' | 'large'} [props.size='medium'] - 按钮尺寸
* @param {boolean} [props.loading=false] - 是否显示加载状态
* @param {boolean} [props.disabled=false] - 是否禁用按钮
* @param {ViewStyle} [props.style] - 额外的容器样式
*/
interface PrimaryButtonProps {
title: string;
onPress: () => void;
size?: 'small' | 'medium' | 'large';
loading?: boolean;
disabled?: boolean;
style?: ViewStyle;
}
const PrimaryButton: React.ForwardRefRenderFunction<TouchableOpacity, PrimaryButtonProps> = (
{ title, onPress, size = 'medium', loading = false, disabled = false, style },
ref
) => {
const theme = useTheme();
// 根据尺寸和状态决定样式
const getButtonStyle = (): ViewStyle => {
const baseStyle: ViewStyle = {
backgroundColor: disabled ? theme.colors.disabled : theme.colors.primary,
borderRadius: theme.borderRadius.medium,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
};
const sizeMap: Record<PrimaryButtonProps['size'], { paddingVertical: number; paddingHorizontal: number }> = {
small: { paddingVertical: theme.spacing.xs, paddingHorizontal: theme.spacing.sm },
medium: { paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md },
large: { paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.lg },
};
return { ...baseStyle, ...sizeMap[size], ...style };
};
const getTextStyle = (): TextStyle => ({
color: theme.colors.onPrimary,
fontSize: theme.typography.button[size],
fontWeight: '600',
marginLeft: loading ? theme.spacing.xs : 0,
});
const handlePress = () => {
if (!disabled && !loading) {
onPress();
}
};
return (
<TouchableOpacity
ref={ref}
style={getButtonStyle()}
onPress={handlePress}
disabled={disabled || loading}
accessible={true}
accessibilityLabel={loading ? `${title},加载中` : title}
accessibilityRole="button"
>
{loading && <ActivityIndicator size="small" color={theme.colors.onPrimary} />}
<Text style={getTextStyle()}>{loading ? '处理中...' : title}</Text>
</TouchableOpacity>
);
};
export default React.forwardRef(PrimaryButton);
这个示例向AI展示了几个关键模式:使用TypeScript接口定义强类型的Props、从主题上下文获取样式、实现完整的可访问性支持、处理加载和禁用状态。当开发者后续请求创建其他组件(如Card、InputField)时,AI会遵循同样的结构和规范进行输出。
4. 物联网设备管理后台:处理异构数据与复杂状态机
物联网项目通常需要对接成百上千种型号、协议各异的设备,后台系统要处理海量的遥测数据、设备状态变更和反向控制指令。这里的GEMINI.md需要帮助AI理解“物模型”的概念和复杂的状态流转逻辑。
4.1 上下文:抽象设备,定义统一语言
项目核心:构建一个管理智能家居设备的云平台,支持设备注册、状态监控、固件升级和规则引擎。核心挑战是设备协议的多样性和设备状态管理的复杂性。
技术架构与选型:
- 设备接入:MQTT Broker (EMQX)。选型理由是MQTT协议轻量、适合物联网场景,EMQX支持集群和高并发连接。
- 数据管道:Apache Kafka,用于接收设备上报的海量遥测数据(Telemetry)和事件(Event)。
- 核心服务:使用Go编写设备影子(Device Shadow)服务。选型理由是Go的高并发能力适合处理大量设备连接。
- 数据存储:时序数据库TDengine存储设备遥测数据,PostgreSQL存储设备元数据、关系和状态。选型理由是TDengine对时序数据的高效处理能力。
- 物模型:采用JSON Schema定义设备的属性(Properties)、服务(Services)和事件(Events),这是平台与设备交互的“合约”。
4.2 行为准则:成为物联网领域的“协议转换器”
AI角色定义:你是一位物联网平台架构师,擅长将物理世界的设备抽象为数字世界的模型。你对MQTT、CoAP等物联网协议有深刻理解,并善于设计最终一致性的分布式状态管理系统。
关键设计规则:
- 设备影子模式:所有设备状态必须通过“设备影子”服务进行中介。设备上报状态更新影子“期望状态”,应用下发指令也是修改影子“期望状态”,由影子服务负责与设备同步,解决网络不稳定带来的状态不一致问题。
- 物模型驱动:所有设备相关的业务逻辑(如数据解析、指令下发)必须基于物模型(Thing Model) 的定义来开发,不能硬编码设备型号。
- 消息处理:处理设备消息时,必须考虑消息的QoS等级和可能的重试机制。对于关键指令(如固件升级),必须实现确认和超时重传逻辑。
- 数据降采样:长期存储的遥测数据,必须提供降采样策略(如将1秒级数据聚合成1分钟、1小时的平均值),以节省存储空间。
4.3 示例:实现一个设备影子状态更新服务
让我们通过一个设备影子服务处理状态更新的例子来训练AI。
用户提问: “设备通过MQTT上报了它的在线状态和温度属性,影子服务该如何处理这条消息并更新数据库?”
期望的AI回答:
// internal/shadow/handler.go
package shadow
import (
"encoding/json"
"context"
"github.com/emqx/emqx-webhook-client/client"
"gorm.io/gorm"
"log"
)
// DeviceShadow 表示设备影子结构
type DeviceShadow struct {
gorm.Model
DeviceID string `gorm:"uniqueIndex"`
Reported JSONB `gorm:"type:jsonb"` // 设备上报状态
Desired JSONB `gorm:"type:jsonb"` // 应用期望状态
Metadata JSONB `gorm:"type:jsonb"` // 时间戳等元数据
Version int64 // 影子版本,用于乐观锁
ConnectionStatus string // 在线/离线
}
// HandleDeviceReport 处理设备上报消息
func (s *ShadowService) HandleDeviceReport(ctx context.Context, msg *client.Message) error {
var report map[string]interface{}
if err := json.Unmarshal(msg.Payload, &report); err != nil {
return err
}
deviceID := msg.Topic.GetDeviceID() // 假设从Topic解析出设备ID
if deviceID == "" {
return errors.New("invalid topic, cannot extract device id")
}
// 1. 根据物模型验证上报数据
thingModel, err := s.modelRepo.GetByDeviceID(ctx, deviceID)
if err != nil {
return err
}
if !thingModel.ValidateReported(report) {
return errors.New("reported data does not conform to thing model")
}
// 2. 开启事务,更新设备影子
tx := s.db.WithContext(ctx).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
var shadow DeviceShadow
// 使用乐观锁防止并发更新冲突
result := tx.Where("device_id = ?", deviceID).First(&shadow)
if result.Error != nil {
// 设备影子不存在,创建新的
shadow = DeviceShadow{
DeviceID: deviceID,
Reported: JSONB(report),
ConnectionStatus: "online",
Version: 1,
}
if err := tx.Create(&shadow).Error; err != nil {
tx.Rollback()
return err
}
} else {
// 更新现有影子
updateData := map[string]interface{}{
"reported": report,
"connection_status": "online",
"version": gorm.Expr("version + 1"),
"metadata": gorm.Expr(`jsonb_set(metadata, '{reported, timestamp}', to_jsonb(now()), true)`),
}
// 使用版本号进行乐观锁更新
if err := tx.Model(&DeviceShadow{}).Where("device_id = ? AND version = ?", deviceID, shadow.Version).Updates(updateData).Error; err != nil {
tx.Rollback()
return err // 更新失败,可能版本冲突,由调用方决定重试或告警
}
}
// 3. 检查上报状态与期望状态是否一致,若一致则清除期望状态(delta同步完成)
if shadows.ReportedMatchesDesired(shadow.Reported, shadow.Desired) {
if err := tx.Model(&shadow).Update("desired", nil).Error; err != nil {
tx.Rollback()
return err
}
}
// 4. 将状态变更事件发布到Kafka,供规则引擎等其他服务消费
event := ShadowUpdatedEvent{DeviceID: deviceID, Reported: report, Timestamp: time.Now()}
if err := s.producer.SendEvent(ctx, event); err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
提示:这个示例涵盖了物联网后台的核心模式:物模型验证、影子文档的乐观锁更新、状态同步(delta处理)以及事件驱动架构。AI在理解这个模式后,对于处理设备离线、指令下发、固件升级状态跟踪等场景,都能举一反三。
5. 内容管理系统(CMS)与无头架构:解耦与灵活性的艺术
现代内容管理不再局限于简单的文章发布,而是需要支持多终端(Web、APP、小程序)、多渠道分发的“无头CMS”(Headless CMS)。GEMINI.md在这里要指导AI如何设计一个内容模型驱动、API优先、高度可扩展的后台系统。
5.1 上下文:拥抱无头CMS的设计哲学
项目愿景:构建一个为公司官网、博客、帮助中心、移动端提供统一内容源的后台系统。编辑人员在一个后台创建内容,多个前端通过API消费。核心需求是内容模型的灵活定义和强大的版本管理能力。
技术选型与架构:
- 核心框架:Strapi (Node.js) 或 Directus。选型理由是它们提供了开箱即用的无头CMS能力,支持自定义内容类型、角色权限和REST/GraphQL API。我们选择基于Strapi进行二次开发。
- 数据库:PostgreSQL。选型理由是对JSONB字段的良好支持,便于存储灵活的内容结构。
- 前端消费:Next.js (SSG/SSR) 用于官网,React Native用于APP。它们都通过GraphQL API从Strapi获取内容。
- 媒体存储:AWS S3 或 Cloudinary。选型理由是可扩展的对象存储,支持图片、视频等资源的优化和CDN分发。
- 部署:Docker容器化,部署在Kubernetes上,便于水平扩展。
5.2 行为准则:塑造内容领域的“建模专家”
AI角色定义:你是一位精通无头CMS和现代前端架构的全栈开发者。你深刻理解“内容即数据”的理念,善于将复杂的业务内容抽象为可复用、可组合的内容模型(Content Type)。你注重API设计和开发体验。
核心开发原则:
- 模型先行:在编写任何代码之前,必须先定义清晰的内容模型(Content Type)。每个模型应具有明确的用途,字段设计需考虑前端展示的便利性(如提供富文本、图片、关联字段等)。
- API设计:所有内容API必须支持国际化(i18n) 和草稿/发布状态。提供灵活的过滤、排序、分页参数。优先考虑提供GraphQL API以获得前端数据获取的最大灵活性。
- Webhook与事件驱动:当内容发布、更新或删除时,必须触发Webhook事件,以便同步到搜索引擎、刷新CDN缓存或通知其他系统。
- 权限控制:基于角色的访问控制(RBAC)必须精细到字段级别。例如,市场人员可以编辑文章内容但无法修改SEO设置,而SEO专家则相反。
5.3 示例:定义一个“博客文章”内容模型及其API
通过定义最常见的“博客文章”模型,我们可以展示如何将业务需求转化为技术实现。
用户提问: “我们需要在Strapi中创建一个‘BlogPost’内容类型,支持多语言、作者关联、标签分类,并生成对应的GraphQL API。”
期望的AI回答: 首先,在Strapi后台或通过代码方式定义内容模型 (api/blog-post/content-types/blog-post/schema.json):
{
"kind": "collectionType",
"collectionName": "blog_posts",
"info": {
"singularName": "blog-post",
"pluralName": "blog-posts",
"displayName": "博客文章",
"description": "用于发布博客文章"
},
"options": {
"draftAndPublish": true // 启用草稿/发布功能
},
"pluginOptions": {
"i18n": {
"localized": true // 启用国际化
}
},
"attributes": {
"title": {
"type": "string",
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"slug": {
"type": "uid",
"targetField": "title"
},
"content": {
"type": "richtext",
"required": true
},
"excerpt": {
"type": "text"
},
"coverImage": {
"type": "media",
"multiple": false,
"required": false
},
"publishedAt": {
"type": "datetime"
},
"author": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user", // 关联用户表
"inversedBy": "blogPosts"
},
"tags": {
"type": "relation",
"relation": "manyToMany",
"target": "api::tag.tag", // 关联标签表
"inversedBy": "blogPosts"
},
"seo": {
"type": "component",
"repeatable": false,
"component": "shared.seo" // 复用SEO组件
}
}
}
然后,AI可以进一步说明如何通过Strapi自动生成的GraphQL API进行查询:
# 前端(如Next.js)中的GraphQL查询示例
query GetPublishedBlogPosts($locale: I18NLocaleCode!, $limit: Int!, $start: Int!) {
blogPosts(
locale: $locale
publicationState: LIVE # 只查询已发布的内容
sort: "publishedAt:desc"
pagination: { limit: $limit, start: $start }
) {
data {
id
attributes {
title
slug
excerpt
publishedAt
coverImage {
data {
attributes {
url
formats
}
}
}
author {
data {
attributes {
username
avatar {
data {
attributes { url }
}
}
}
}
}
tags {
data {
attributes {
name
slug
}
}
}
}
}
meta {
pagination {
total
pageSize
pageCount
page
}
}
}
}
注意:这个示例不仅展示了模型定义,还关联了前端消费的GraphQL查询。它向AI传递了无头CMS的核心工作流:定义模型 -> 自动生成管理界面和API -> 前端通过API消费。当需要创建“产品”、“案例”等内容类型时,AI会遵循同样的模式。
通过这五个跨度极大的项目案例,我们看到了GEMINI.md如何像一位经验丰富的导师,在不同领域为AI助手注入特定的领域知识和工程原则。从电商的毫秒级性能要求,到金融的数据洁癖,再到物联网的状态机复杂性,每一份GEMINI.md都是项目独特DNA的编码。其力量不在于语法的复杂性,而在于描述的精确性和思维框架的传递。当你开始为自己的项目编写GEMINI.md时,不妨从回答这几个问题开始:我的项目最不能妥协的原则是什么?我的团队有哪些心照不宣的最佳实践?我最希望AI在哪些地方发挥最大价值?回答这些问题本身,就是一次宝贵的项目复盘与架构梳理。最终,你会发现,最好的GEMINI.md,就是把你最希望团队成员拥有的那种思维模式,清晰无误地告诉你的AI伙伴。
更多推荐



所有评论(0)