首页
壁纸
统计
友链
Search
1
Vue2详细笔记
68 阅读
2
ExpressAPI
34 阅读
3
微信小程序
31 阅读
4
Spring MVC 请求生命周期
30 阅读
5
Nestjs概述-中文
29 阅读
Nodejs
Vue
Java
Msql
登录
Search
Wasnl
累计撰写
40
篇文章
累计收到
1
条评论
首页
栏目
Nodejs
Vue
Java
Msql
页面
壁纸
统计
友链
搜索到
40
篇与
的结果
2026-06-02
Java随心记
一、常用工具与集合操作1.1 Arrays.asList()作用:将数组转换为 List。注意:返回的 List 是固定大小的,不支持 add/remove 操作。List<String> list = Arrays.asList("a", "b", "c");1.2 CollUtil.toMap()(Hutool 工具)把一个 List 转换成 Map。参数1:要转换的列表。参数2:key 的生成方式(如 Stock::getSkuId)。参数3:value 的生成方式,Function.identity() 表示用对象本身作为 value。Map<Long, Stock> map = CollUtil.toMap(stocks, Stock::getSkuId, Function.identity());1.3 IdentityHashMap比较冷门,判断 key 是否相同依据 对象的引用地址(==),而不是 equals() / hashCode()。二、数据查询策略2.1 联表查询 vs 分开查 + Map 组装方式适用场景联表查询数据量不大、关联简单、不需要对查出的用户数据做额外处理分开查 + Map 组装数据分布在不同的服务器、微服务架构,或者需要灵活组装三、Java 集合框架3.1 集合体系概览Collection 是除 Map 外所有集合类的根接口。java.util 包主要提供三种类型的集合:List、Set、Map。统一使用 Iterator 遍历集合。3.2 ListArrayList:基于动态数组,内存连续。LinkedList:基于双向链表,节点有前后指针。List 允许添加 null。使用 List.of() 创建不可变 List(不支持 null 元素)。List<Integer> list = List.of(1, 2, 5);Iterator 遍历:boolean hasNext() 判断是否有下一个元素。E next() 返回下一个元素。boolean contains(Object o) 判断是否包含某元素。int indexOf(Object o) 返回元素索引,不存在返回 -1。重要:使用 contains()、indexOf() 等方法时,元素类必须正确覆写 equals()。String、Integer 等标准库类已经正确实现了 equals(),所以可以直接使用。3.3 编写 equals() 方法理论原则:自反性、对称性、传递性、一致性。实践写法(直接套用):先确定实例相等的逻辑(哪些字段相等就算相等)。用 instanceof 判断类型,类型匹配后再比较字段。使用 Objects.equals() 比较引用类型字段,避免空指针。public boolean equals(Object o) { if (o instanceof Person p) { return Objects.equals(this.name, p.name) && this.age == p.age; } return false; }3.4 MapMap 遍历是无序的。底层是哈希表,通过哈希函数将 key 转换成数组下标,直接定位,查找速度极快 O(1)。作为 key 的对象必须正确覆写 equals() 和 hashCode(),相等的 key 调用 equals 必须返回 true。3.5 Queue 队列实现先进先出(FIFO)的数据结构。常用方法:add() / offer():添加元素到队尾。remove() / poll():从队首获取并删除元素。element() / peek():从队首获取但不删除。避免将 null 添加到队列。四、编码与安全4.1 URL 编码使用 java.net.URLEncoder 对任意字符串进行 URL 编码。4.2 哈希算法目的:验证原始数据是否被篡改。MessageDigest 常见用途:计算文件校验和实现文件秒传存储用户密码防止数据重复生成 API 签名4.3 AES 加密最可靠的 AES 加密模式:AES-GCM。五、多线程与并发5.1 线程基础守护线程(Daemon)JVM 退出时不等待守护线程结束,直接强制终止。非守护线程(用户线程)结束时 JVM 才会退出。设置方法:在 start() 前调用 setDaemon(true)。核心业务线程不能设置为守护线程。典型用途:垃圾回收、日志监控、心跳检测。t.join()让当前线程等待 t 线程执行结束。生产环境禁用,容易造成阻塞,推荐用 Future 等异步方式。Thread.sleep(long millis)静态方法,哪个线程调用,哪个线程睡觉。线程中断线程结束即 run() 方法执行完毕。目标线程通过检查 isInterrupted() 标志判断自身是否已被中断。volatile 与内存可见性变量的值保存在主内存,线程访问时会拷贝到工作内存。修改后的值何时回写主内存不确定,因此会出现可见性问题。synchronized 不能修饰变量,要保证变量跨线程可见性,使用 volatile。注意:volatile 只能保证可见性和禁止指令重排,不保证原子性。5.2 同步机制synchronizedJava 语言层面语法,不需要手动释放锁(退出同步块自动释放)。修饰方法时,锁对象是 this(实例方法)或类对象(静态方法)。配合 wait() / notifyAll() 实现线程等待/唤醒(了解即可,实际多用 Lock 体系)。ReentrantLockJava 代码实现的锁,必须在 finally 中手动释放。标准用法:private final Lock lock = new ReentrantLock(); lock.lock(); try { // 同步代码 } finally { lock.unlock(); }配合 Condition 的 await() / signalAll() 实现等待/通知。ReadWriteLock提升读多写少场景的并发性能。规则:读-读 可共存写-写 互斥读-写 互斥(要么全是读,要么只有一个写)实现:private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); private final Lock rlock = rwlock.readLock(); private final Lock wlock = rwlock.writeLock(); // 写操作 wlock.lock(); try { ... } finally { wlock.unlock(); } // 读操作 rlock.lock(); try { ... } finally { rlock.unlock(); }注意:读锁是悲观锁,频繁读可能导致写线程饥饿。StampedLock三种模式:写锁、悲观读锁、乐观读。乐观读 tryOptimisticRead() 完全不阻塞写线程,先获取版本号再读数据,读完后验证版本号有无变化,变化则重试或升级为悲观读。不可重入(一个线程持有写锁再尝试获取读锁会死锁)。Semaphore信号量,限制同时访问某个资源的线程数量。用法:final Semaphore semaphore = new Semaphore(3); // 最多3个许可 public String access() throws Exception { semaphore.acquire(); // 获取许可,可能阻塞 try { // 业务逻辑 } finally { semaphore.release(); // 释放许可 } }tryAcquire(long timeout, TimeUnit unit) 可指定等待时间。5.3 线程安全集合接口非线程安全线程安全替代ListArrayListCopyOnWriteArrayListMapHashMapConcurrentHashMapSetHashSet / TreeSetCopyOnWriteArraySetQueueArrayDeque / LinkedListArrayBlockingQueue / LinkedBlockingQueueDequeArrayDeque / LinkedListLinkedBlockingDeque5.4 线程池ExecutorService经典用法:ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(task1); ... executor.shutdown(); // 等待任务完成后关闭常用线程池及底层原理:FixedThreadPool:核心线程数 = 最大线程数 + 无界队列CachedThreadPool:核心线程数 0,最大线程数无限 + 同步移交队列SingleThreadExecutor:核心和最大线程数均为 1 + 无界队列ScheduledThreadPool:ScheduledThreadPoolExecutor,支持定时调度生产推荐:直接使用 ThreadPoolExecutor 定制参数,队列必须有界。public ThreadPoolExecutor( int corePoolSize, // CPU密集:n+1 IO密集:2n int maximumPoolSize, // ≥ corePoolSize long keepAliveTime, // 非核心线程空闲存活时间,常用30-60s TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 有界队列,如 ArrayBlockingQueue ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )拒绝策略策略行为AbortPolicy(默认)丢弃任务,抛 RejectedExecutionExceptionCallerRunsPolicy由提交任务的线程自行执行,可减缓提交速度(推荐)DiscardPolicy静默丢弃任务,不抛异常DiscardOldestPolicy丢弃队列最老的任务,重新提交当前任务推荐配置:CallerRunsPolicy + 监控告警。注意事项创建线程只有一种方式:new Thread().start()。生产环境只用线程池提交任务,禁止手动 new Thread()。for 循环中调用 t.start() 会立即返回,不会等待线程 run() 执行完。5.5 Future 与异步编程Callable 与 Runnable 的区别:Callable 有返回值,可抛出异常。提交 Callable 返回 Future<V>。get():获取结果(可能阻塞)get(long timeout, TimeUnit unit):限时获取cancel(boolean mayInterruptIfRunning):取消任务isDone():判断任务是否完成CompletableFuture实现 Future 和 CompletionStage,支持函数式异步编程。方法引用示例:Main::fetchPrice 等价于 () -> fetchPrice(),常用在 CompletableFuture.supplyAsync(Main::fetchPrice) 中。六、JDBC 简介各数据库厂商使用统一接口,Java 代码无需针对不同数据库分别开发。Maven 依赖:<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> <!-- 运行时才需要此包 --> </dependency>建立连接:DriverManager.getConnection()(自动扫描 classpath 中的驱动)。重要提示:数据库自身就是并发专家,不要用 Java 层锁去干涉数据库操作,除非操作对象是内存数据。
2026年06月02日
11 阅读
0 评论
1 点赞
2026-06-02
Java 反射与动态代理 详细笔记
Java 反射与动态代理 详细笔记一、反射的核心思想反射:在程序运行时,动态获取某个类的完整信息(类名、方法、字段、父类、接口等),并能动态创建对象、调用方法、访问字段。不需要在编译时知道具体类名,可以在运行时根据字符串类名加载类。常用于框架(Spring、MyBatis)、通用工具(IDE 自动补全)、动态代理等。二、Class 类:一切反射的入口2.1 每个类都有唯一的 Class 对象JVM 在第一次加载一个类时,会在内存中为该类创建一个 Class 实例(俗称“说明书”)。这个 Class 实例保存了该类的全部信息(类名、包、方法、字段、父类、接口等)。无论通过哪种方式获取,同一个类的 Class 实例是同一个对象(可用 == 比较)。2.2 获取 Class 实例的三种方法// 方法1:通过类名.class(最直接,编译时已知类) Class<String> cls1 = String.class; // 方法2:通过实例的 getClass() 方法 String s = "hello"; Class<?> cls2 = s.getClass(); // 实际返回 String.class // 方法3:通过 Class.forName(完整类名)(运行时根据字符串加载) Class<?> cls3 = Class.forName("java.lang.String");三者获得的是同一个 Class 对象:cls1 == cls2 == cls3三、通过反射访问字段(Field)3.1 获取字段的方法(返回值是 java.lang.reflect.Field)方法说明能否得到父类字段getField(String name)获取 public 字段(包括父类)✅ 能getDeclaredField(String name)获取当前类声明的任意字段(不限访问修饰符)❌ 不能,只看当前类getFields()获取所有 public 字段(包括父类)✅ 能getDeclaredFields()获取当前类声明的所有字段(不包括父类)❌ 不能3.2 访问私有字段通过 getDeclaredField 拿到私有 Field 对象后,调用 setAccessible(true) 打破封装。然后可以 get(obj) / set(obj, value) 读写该私有字段。class Person { private String name; } Class<Person> cls = Person.class; Field field = cls.getDeclaredField("name"); field.setAccessible(true); // 允许访问私有字段 Person p = new Person(); field.set(p, "Alice"); String name = (String) field.get(p); // "Alice"四、通过反射调用方法(Method)4.1 获取方法的方法(返回值是 java.lang.reflect.Method)方法说明包括父类getMethod(String name, Class<?>... parameterTypes)获取某个 public 方法✅ 包括getDeclaredMethod(...)获取当前类声明的某个方法(不限修饰符)❌ 不包括getMethods()获取所有 public 方法✅ 包括getDeclaredMethods()获取当前类声明的所有方法❌ 不包括4.2 调用方法拿到 Method 对象后,调用 invoke(Object obj, Object... args)。如果方法是 static,第一个参数传 null。// 示例:调用 String 的 length() 方法 String str = "hello"; Method m = String.class.getMethod("length"); int len = (int) m.invoke(str); // len = 5五、通过反射调用构造器(Constructor)5.1 获取构造器方法说明getConstructor(Class<?>... parameterTypes)获取某个 public 构造器getDeclaredConstructor(...)获取任意修饰符的构造器(包括 private)getConstructors() / getDeclaredConstructors()获取所有构造器5.2 创建实例通过 Constructor.newInstance(Object... initargs) 创建对象。比 Class.newInstance() 更灵活(可以传参数,可以调用非 public 构造器)。// 调用带参数的构造器 Constructor<String> cons = String.class.getConstructor(String.class); String s = cons.newInstance("abc"); // 相当于 new String("abc") // 调用私有构造器(单例模式等) Constructor<MyClass> cons2 = MyClass.class.getDeclaredConstructor(); cons2.setAccessible(true); MyClass obj = cons2.newInstance();注意:Class.newInstance() 已过时(只能调用无参 public 构造器),推荐使用 Constructor.newInstance()。六、获取继承关系方法作用示例Class.getSuperclass()返回父类的 Class 对象Integer.class.getSuperclass() → Number.classClass.getInterfaces()返回当前类直接实现的接口数组Integer.class.getInterfaces() → Comparable, Constable, ConstantDescClass.isAssignableFrom(Class<?> cls)判断当前类是否可接收 cls 类型的对象(即 cls 能否向上转型为当前类)Number.class.isAssignableFrom(Integer.class) → true6.1 关于接口的特殊说明对接口调用 getSuperclass() → 返回 null。获取接口的父接口必须用 getInterfaces()。接口的 isAssignableFrom 用法与类一致:Serializable.class.isAssignableFrom(Integer.class) → true。6.2 instanceof 与 isAssignableFrom 的区别instanceof 需要对象实例:obj instanceof NumberisAssignableFrom 需要Class 对象:Number.class.isAssignableFrom(Integer.class)七、动态代理(Dynamic Proxy)7.1 解决的问题不手动编写接口的实现类,就能在运行时动态创建实现了该接口的对象。典型应用:AOP(面向切面编程)、RPC 框架、日志/事务拦截等。7.2 限制条件只能代理接口(不能代理普通类)。如果需要代理普通类,可使用 CGLIB 或 ByteBuddy。7.3 核心 APIjava.lang.reflect.Proxyjava.lang.reflect.InvocationHandler(函数式接口)7.4 实现步骤定义接口(必须,告知代理对象有哪些方法)。实现 InvocationHandler:重写 invoke 方法,定义当接口方法被调用时实际执行的代码。调用 Proxy.newProxyInstance 创建代理实例。将代理实例转型为接口类型,然后正常调用方法。7.5 示例代码(完整)// 1. 接口 public interface Hello { void morning(String name); } // 2. 使用动态代理(不写 HelloWorld 类) import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicProxyDemo { public static void main(String[] args) { // 2.1 定义调用处理器 InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("morning")) { String name = (String) args[0]; System.out.println("Good morning, " + name); } return null; // 方法返回 void,所以返回 null } }; // 2.2 创建代理对象 Hello hello = (Hello) Proxy.newProxyInstance( Hello.class.getClassLoader(), // 类加载器 new Class[]{Hello.class}, // 要实现的接口列表 handler // 调用处理器 ); // 2.3 调用方法(实际执行 handler.invoke) hello.morning("Bob"); // 输出:Good morning, Bob } }7.6 动态代理内部原理Proxy.newProxyInstance 会在运行时动态生成一个新的类(例如 $Proxy0)。这个类实现了指定的接口(Hello),并且每个方法的实现都是:public void morning(String name) { super.h.invoke(this, method_morning, new Object[]{name}); }super.h 就是你传入的 InvocationHandler。然后通过 ClassLoader 加载这个类,并实例化返回。换句话说:动态代理 = JVM 自动帮你写了一个实现类,并用反射调用你的 handler。7.7 动态代理与反射的关系动态代理生成的新类($Proxy0)本身也是一个类,JVM 会为它创建 Class 对象。你可以对这个 Class 对象进行任何反射操作。InvocationHandler.invoke 方法中收到的 Method 参数就是通过反射得到的。所以动态代理是反射机制的一个典型应用,而不是替代品。八、总结:什么时候用什么技术?场景推荐技术已知类名,需要调用它的方法/访问字段直接调用,无需反射类名字符串在运行时才确定,需要创建实例Class.forName() + Constructor.newInstance()需要遍历一个对象的所有字段/方法(例如写序列化工具)getDeclaredFields() / getDeclaredMethods()不写实现类,仅通过接口定义行为动态代理需要在方法调用前后统一添加逻辑(日志、事务)动态代理 + InvocationHandler最后记住一句话:反射让你在运行时“看见”并“操作”一个类;动态代理让你在运行时“凭空造出”一个实现了接口的类,并把所有方法调用委托给你写的处理器。
2026年06月02日
10 阅读
0 评论
1 点赞
2026-05-27
test
AI Native 实战开发流程:以“管理员后台”为例(通用模板)本流程基于《AI Native 开发治理笔记》的治理理念,以 v3 管理后台 的一个典型功能“退款审核”为案例,展示如何用 AI 工程化方法交付一个安全、合规、可积累的生产级功能。该流程可复用到任何管理员后台的 CRUD、审批、报表、导出等场景。一、总体开发阶段概览阶段核心动作参与角色产出物(存入对应目录)1. 知识准备补充原始资料与业务规则人docs/、business/2. 任务规划编写 task 与选择 workflow人ai/tasks/current/、ai/workflows/3. AI 分析对齐让 AI 只分析,不写代码,对齐方案AI + 人分析报告(对话产物)4. AI 开发实现AI 按约束编码AI代码变更(apps/)5. 质量审查AI 自检 + 人复核AI + 人ai/reviews/6. 知识沉淀提炼通用经验与开发日志AI + 人ai/engineering-knowledge/、ai/development-logs/7. 交付上线最终验证与合并人可运行代码二、前置条件:工程骨架已搭建假设团队已经按治理笔记建立了以下目录和基础文件:v3-system/ ├── apps/ # 现有管理后台代码 ├── docs/ # 已有数据库 DDL、API 文档 ├── business/ # 已有 order/wallet/refund 等业务规则 ├── ai/ │ ├── tasks/current/ │ ├── workflows/ │ ├── reviews/ │ ├── engineering-knowledge/ │ └── development-logs/ ├── AGENTS.md # 已编写全局 AI 规则 └── CLAUDE.md如果还没有,必须先按笔记完成骨架搭建,否则后续流程会因为缺少约束而失控。三、实战步骤详解(以退款审核为例)阶段 1:知识准备——让 AI 有据可依目标:确保 AI 看到的“真相”是完整且准确的。人需要做的事情:检查 docs/是否存在 refund_order 表的 DDL?字段、索引、注释是否完整?是否有关联的 wallet、wallet_transaction 表结构?支付回调相关的第三方文档是否可访问?如果不全,立即用 mysqldump --no-data 导出最新结构放入 docs/db/。编写/更新 business/refund/rules.md 按照笔记模板,补充退款专属规则,例如:# 退款规则 1. 仅支付成功订单可退款 2. 退款必须审核(状态机:PENDING_AUDIT → APPROVED / REJECTED) 3. 退款执行时原子增加用户余额(UPDATE wallet SET balance = balance + ? WHERE user_id = ?) 4. 幂等:退款单 APPROVED 后重复请求直接返回成功 5. 记录 wallet_transaction 类型为 REFUND检查 business/wallet/invariants.md 确认余额不变量(balance >= 0)等规则已存在。产出物:更新后的 docs/ 和 business/,AI 的“知识底座”建立完毕。阶段 2:任务规划——画好 AI 的“活动圈”目标:精确控制 AI 本次能做什么、不能做什么。人需要做的事情:创建任务文件 ai/tasks/current/TASK-REFUND-APPROVE.md 按模板填写(关键字段见笔记第七章),示例要点:Goal:开发退款审核通过接口Allowed Modules:RefundController、RefundService、RefundDao,表 refund_order, wallet, wallet_transactionForbidden Modules:auth, distributor, goods,以及任何前端代码Technical Requirements:JdbcTemplate、@Transactional、Redis 锁、幂等Deliverables:修改的文件、SQL 变更、风险点选择或编写 workflow 如果 ai/workflows/add-admin-api.md 已存在,则沿用;否则参考笔记第八章编写一个。workflow 定义了标准开发步骤:分析 → 方案 → 编码 → 自检。产出物:TASK-REFUND-APPROVE.md 和确认使用的 workflow。阶段 3:AI 分析对齐——让 AI 先“说出来”再动手目标:AI 在不写代码的前提下,给出完整技术方案,由人确认,避免方向错误。操作步骤:新建 AI 会话,使用分析 Prompt(复制以下内容)请按顺序读取以下文件: 1. AGENTS.md 2. business/refund/rules.md 3. business/wallet/invariants.md 4. ai/workflows/add-admin-api.md 5. ai/tasks/current/TASK-REFUND-APPROVE.md 目标:理解系统约束和本次任务边界。 现在,仅进行分析设计,**禁止编写任何实现代码**。 请输出: 1. API 设计草案(POST /api/admin/refund/approve,请求体 refundOrderId,返回体通用Result) 2. SQL 设计草案(查询退款单、更新状态、原子更新钱包余额、插入流水) 3. 事务边界:退款单状态更新 + 余额更新 + 流水插入必须在同一事务 4. 并发控制方案:Redis分布式锁,key = lock:refund:approve:{refundOrderId},超时30s 5. 风险点分级(如:余额并发更新 -> 已用原子SQL规避;锁未释放导致死锁 -> finally释放) 6. 需要澄清的业务点(如:退款金额是否包含优惠券部分?我们假设仅退现金)审核 AI 输出的分析报告 重点检查:SQL 是否使用原子更新而非 select-then-update?事务是否覆盖了所有需要一致性的操作?幂等逻辑是否只是状态检查(可靠吗?)?结合 Redis 锁后的行为?权限校验是否遗漏(Controller 上要加 RBAC 注解)?修改方案直到满意,若有业务不确定点,拉产品经理确认。产出物:一份人工确认过的技术方案(可保存在 ai/reviews/ 下作为设计记录)。阶段 4:AI 开发实现——戴着手铐跳舞目标:AI 严格遵守约束,产出符合规范的代码。操作步骤:在同一会话(或保留上下文的新会话)发送开发 Prompt基于我们已确认的方案,现在开始编码。 请严格遵守以下约束: - 仅修改 task 中 Allowed Modules 内的文件 - 遵守 AGENTS.md 所有规则(SQL规则、事务规则、架构分层等) - 使用 JdbcTemplate,保持与现有代码风格一致 - 实现幂等、分布式锁、事务、原子更新 - 任何新增文件需先说明用途 请按顺序产出: 1. DAO 层方法(JdbcTemplate实现) 2. Service 层逻辑(含事务和锁) 3. Controller 层(参数校验、调用Service) 4. 针对每个 SQL 给出 EXPLAIN 输出 5. 自认为的实现风险点监控 AI 产出 人随时检查 AI 是否有越界行为(比如偷偷修改了 WalletController),一旦发现立即叫停并纠正。 通常 AI 会直接给出代码块,将其复制到对应文件或通过工具应用。产出物:修改的 Java 文件(及可能的 SQL 迁移脚本),AI 提供的 EXPLAIN 结果。阶段 5:质量审查——AI 自首 + 人工终审目标:系统性捕捉缺陷,防止带病上线。操作步骤:发送 Review Prompt请对我们刚刚实现的退款审核接口进行完整 Review,严格遵守 AGENTS.md 的 Review 规则。 逐一检查: - SQL 注入 - EXPLAIN 分析(再次确认索引命中) - 事务是否覆盖正确 - 空指针可能点 - 业务不变量(balance >= 0, 状态机) - 幂等性 - Redis 锁的使用(正确加锁、解锁、超时) - RBAC 权限注解是否添加 - 缓存一致性(如果涉及缓存) 请输出结构化 Review 报告,标注每项的通过/不通过/风险及建议。人复核 Review 报告对阻断性问题,让 AI 立即修复,然后重新走一遍轻量 review。存档报告到 ai/reviews/REVIEW-REFUND-APPROVE-20260527.md。产出物:Review 报告,以及修复后的代码。阶段 6:知识沉淀——把教训变成 AI 的长期记忆目标:让这次开发的经验在未来项目中自动生效。操作步骤:提炼 engineering-knowledge 从 Review 报告和开发过程中的坑点提炼通用知识,例如:refund-atomic-balance.md(退款余额原子更新)redis-lock-finally-unlock.md(锁必须 finally 释放)人可手动编写或让 AI 生成:从本次 Review 和开发日志中提取可复用的工程知识,按 ai/engineering-knowledge/ 模板创建文件。生成 development-log请生成本次开发日志,包含已完成功能、遗留风险、关键决策、下一步计划,存入 ai/development-logs/LOG-20260527-REFUND-APPROVE.md。产出物:新的 knowledge 文件和开发日志。阶段 7:交付上线——完成闭环目标:功能正式合入主分支。运行单元测试、集成测试。进行 Code Review(可能还有人工同事)。部署到测试环境验证。上线生产,监控日志和报警。此时,TASK-REFUND-APPROVE.md 移入 ai/tasks/archive/。四、对管理员后台的通用性说明上述流程不限于退款审核,任何后台功能开发皆可套用:新增列表查询:task 约束仅修改列表相关 DAO/Service,workflow 引用 add-admin-query.md,business 关注深分页、数据脱敏规则。导出报表:约束文件生成、异步任务、限流规则。权限/角色管理:重点关注 RBAC 规则、审计日志要求。配置管理:关注缓存刷新、并发修改。只需按照功能点创建对应的 business 规则和 task,选择或定制 workflow,其余审查、知识沉淀、开发日志步骤完全一致。五、核心成功要素回顾约束先行:business、AGENTS、task 必须写清楚再让 AI 动手。分析分离:先出方案、对齐,再编码,避免返工。审查强制:SQL、事务、幂等、并发必须逐项过关。知识积累:每次开发结束必须产出 knowledge,形成复利。记忆外挂:development-log 让 AI 随时恢复上下文,支撑连续迭代。坚持这套流程 2~3 个迭代后,团队会发现:新增后台功能的 AI 出错率大幅下降,开发速度稳步提升,而维护成本反而降低——因为所有规则都已编码在知识体系中,AI 越来越像一个懂业务的资深工程师。
2026年05月27日
8 阅读
0 评论
0 点赞
2026-05-27
AI 开发治理入门笔记(初学者实战版)
目标:你不是学习:“怎么让 AI 帮你写几段代码”而是学习:“怎么真正用 AI 开发一个长期项目”下面这份。我会完全按:真实开发流程给你讲。一、先理解:AI 开发真正的问题是什么?很多新人以为:AI开发是:“会 Prompt”其实不是。真正问题是:AI 有三个巨大问题问题结果AI 会乱改代码改到别的模块AI 会忘记业务状态机错误AI 会忘记上下文第二天继续不了所以:真正的 AI 开发:本质是:管理 AI不是:单纯使用 AI二、真正 AI 工程的核心思想你以后:不要这样:错误方式“帮我开发后台”因为:AI 会:无限扩散改很多文件忘记事务忘记权限忘记 review正确方式你以后:每次只做:一个明确任务例如:开发用户分页接口不是:开发整个后台三、真正完整目录(你以后就这样)v3-system/ ├── apps/ ├── docs/ ├── business/ ├── ai/ │ ├── tasks/ │ ├── workflows/ │ ├── reviews/ │ ├── engineering-knowledge/ │ └── development-logs/ ├── AGENTS.md └── CLAUDE.md下面。我一个一个给你解释。四、docs(原始资料)作用存:项目原始资料例如:docs/ ├── auth.sql ├── business.sql ├── 管理员API.md └── 产品需求.md谁创建?你AI 能修改吗?不能为什么?因为:docs 是:原始真相AI:不能:修改真相五、business(最重要)这个:是:整个系统最重要的东西business 是什么?business:保存:业务规则例如:订单状态钱包规则分销规则提现规则为什么一定需要 business?因为:AI:根本不懂你的业务它只能猜。举例如果:你没有 business。AI 可能:允许:已完成订单退款但实际上:你业务:禁止完成后退款所以:业务规则:必须:你定义六、business 实际怎么写?文件business/order.md内容(真实版)# Order State Machine CREATED → PAID → DELIVERING → FINISHED 退款: PAID → REFUND_PENDING → REFUNDED 禁止: - FINISHED退款 - CREATED退款七、AGENTS.md(AI宪法)这个:超级重要。它是什么?它是:AI全局规则类似:公司制度文件AGENTS.md内容(初学者推荐版)# Global AI Rules ## SQL Rules - Never use SELECT * - Never update without WHERE - Always check indexes --- ## Scope Rules Never modify unrelated modules. --- ## Architecture Rules - Controller handles params only - Service handles business only - DAO handles SQL only --- ## Transaction Rules Critical operations must use transactions. --- ## Review Rules Always review: - transaction - sql - security - invariants八、为什么 AGENTS 很重要?因为:AI:非常容易乱来例如:你开发:用户分页AI:突然:改 auth改 wallet改 distributor很常见。所以:必须:提前立规矩九、tasks(最关键)本质本次任务 + AI边界为什么 task 最重要?因为:它决定:AI 允许做什么task 文件ai/tasks/admin-user-page.mdtask 内容(真实版)# Goal 开发: GET /admin/user/page --- # Features 支持: - phone - status - create_time 分页: - page - pageSize --- # Allowed Modules - user --- # Forbidden Modules - auth - wallet - distributor --- # Requirements 1. JdbcTemplate 2. Explain SQL 3. Prevent SQL Injection 4. Soft Delete Only --- # Deliverables Output: - modified files - sql - risks十、workflow(SOP)很多新人:最容易误解这个。workflow 不是任务workflow:不是:“开发用户分页”而是:“新增后台接口应该怎么做”workflow 本质AI工作流程模板文件ai/workflows/add-admin-api.md内容# Add Admin API Workflow ## Analysis 1. Analyze database 2. Check indexes 3. Check transactions 4. Check permissions --- ## Design Output: - api path - params - response - sql - risks --- ## Development 1. DAO 2. Service 3. Controller --- ## Review Must review: - explain - sql injection - RBAC - invariants十一、workflow 什么时候用?开始任务前例如:开发:退款接口AI:先读取:workflow知道:正确开发步骤十二、真正完整开发流程(重点)现在。真正开发开始。Step 1:你先看 docs理解:SQLAPI产品Step 2:你写 business定义:业务真相例如:退款规则。Step 3:你写 task限制:AI工作边界Step 4:AI读取 workflow学习:标准开发流程Step 5:第一次 AI 对话(分析)Prompt(你直接背)请读取: - AGENTS.md - business/order.md - ai/workflows/add-admin-api.md - ai/tasks/refund-api.md 现在: 只进行分析。 禁止开发。 输出: 1. API设计 2. SQL设计 3. 索引检查 4. 风险点 5. 事务分析 6. review建议为什么先分析?因为:AI:直接开发很危险Step 6:你审核分析结果检查:SQL风险事务是否符合业务Step 7:第二次 AI 对话(开发)Prompt(必背)请读取: - AGENTS.md - business/order.md - ai/workflows/add-admin-api.md - ai/tasks/refund-api.md 根据已确认方案。 现在开始开发。 要求: 1. 先DAO 2. 再Service 3. 最后Controller 输出: 1. 修改文件 2. explain sql 3. 风险 4. review建议Step 8:第三次 AI 对话(Review)Prompt(必背)review 当前模块。 检查: - explain - sql injection - indexes - null pointer - transaction - RBAC - idempotent - redis consistency - deep paginationreviews 保存什么?文件ai/reviews/refund-review.md内容# Refund Review 1. Missing Redis Lock 2. Callback not idempotent 3. Wallet rollback risk十三、engineering-knowledge(长期经验)review 后。你发现:支付回调容易重复这是:长期经验于是:保存:文件ai/engineering-knowledge/payment.md内容# Payment Callback 支付回调必须幂等。 原因: 支付平台会重复回调。十四、development-logs(开发记忆)文件ai/development-logs/refund-api.md内容# Refund API Development Log Completed: - refund api - sql review Risks: - no retry queue Next: - add retry mechanism十五、为什么必须有 DevelopmentLog?因为:AI:会失忆第二天:你继续开发。AI 读取:development-log快速恢复上下文。十六、真正成熟后的核心思想(最重要)你以后:不要认为:AI工程 = Prompt真正成熟后:AI工程:本质是:规则 + 边界 + SOP + Review + Knowledge + 记忆十七、最后一个真正重要的建议(一定记住)你现在:不要追求:“最完整治理”真正长期能跑的:一定是:最小必要治理先:能稳定开发再:慢慢增加规则这是:真正企业级 AI Native 开发
2026年05月27日
8 阅读
0 评论
1 点赞
2026-05-25
Agentic Coding 第一性原理笔记
Agentic Coding 第一性原理笔记一、LLM 的本质大语言模型的工作方式可概括为:预测下一个 token。自回归生成过程:计算当前位置与上下文中每个位置的相关程度相关性高的位置获得更高权重,形成概率分布根据权重加权聚合上下文信息,预测下一个 token关键需求:提供清晰的成功标准(明确的奖励信号)允许试错,观察模型的决策模式二、Coding Agent 的典型工具文件操作:read_file、write_file、edit_file(支持局部编辑)代码执行:shell/terminal(运行命令、安装依赖、执行测试)代码搜索:grep/search(文本/模式搜索)、semantic_search(语义搜索)项目导航:list_directory、find_files工具的粒度、参数设计、返回格式直接决定 Agent 的能力边界。三、常见挑战与解决方案1. 会话间失忆现象:会话之间无持久记忆。方案:使用结构化任务追踪(如 issue tracker)会话结束生成状态摘要,供下次使用将重要决策和发现记录在固定位置(而非对话历史)2. 上下文窗口耗尽方案:精简工具输出,返回摘要或相关片段简化工具 schema,避免深度嵌套结构将大任务分解为子任务,每个使用新上下文3. 有效上下文远小于标称值即使 1M token 窗口,实际有效利用仅 10-15%。「Dumb Zone」:窗口中间 40-60% 区域召回率下降、推理变差(Lost in the Middle)。策略:将上下文利用率控制在 15% 以内可显著缓解。4. 新发现的任务被丢弃现象:LLM 发现问题但上下文紧张时选择忽略。方案:提供外部工具,让 Agent 随时记录发现任务结束时要求列出未处理的问题建立“发现即记录”的流程5. 过早宣告完成方案:将整体计划保存在上下文外的固定位置定期对照原始计划检查进度使用结构化任务追踪,而非依赖 Agent 记忆四、最佳实践1. 短对话优于长对话核心原则:保持对话简短、专注,每个对话只做一件事。以对话为工作单位:功能:用户登录后的会话管理 [对话1] 调研现有代码结构 → 输出关键文件列表 [对话2] 实现基础功能 → 核心 session 保存逻辑 [对话3] 添加错误处理 → 边界情况处理 [对话4] 编写测试 → 单元测试与集成测试 [对话5] 代码审查 → 检查规范与安全 [对话6] 清理与重构 → 根据审查调整对话间上下文共享:引用之前对话的结论利用 Git 状态(查看 diff 或提交记录)将决策记录在 AGENTS.md 等文件直接 #mention 相关文件不要在一个对话完成所有事;当任务完成或对话混乱时,立即开始新对话。2. 编写有效的项目配置文件回答三个问题:WHAT(技术栈/结构)、WHY(设计决策)、HOW(运行/测试命令)少即是多:前沿模型可靠遵循约 150-200 条指令,Agent 系统提示已占用约 50 条。✅ 简洁 + 指向详细文档❌ 塞满所有规范细节渐进式披露:agent_docs/ ├── building_the_project.md ├── running_tests.md ├── code_conventions.md ├── service_architecture.md └── database_schema.md偏好指针而非副本:用 file:line 引用权威代码位置,避免文档中嵌入过时代码片段。不要让 Agent 做 Linter 的工作:代码风格应交由自动化工具,不写进配置文件。花费时间仔细考量配置文件的每一行,是 ROI 最高的投资之一。3. 实践建议对话超过 80K-100K token → 开始新对话完成独立子任务后 → 开始新对话Agent 出现“醉酒”症状(重复、遗忘、偏离目标) → 立即开始新对话将“开始新对话”视为正常流程,而非失败重试五、Compounding Engineering(复利工程)核心理念:不只是在解决问题,而是在教育系统。每个 PR、bug、代码审查都应成为永久教训,更新 Agent 的默认行为。实践方式将经验沉淀到项目文档AGENTS.md 示例:## 代码风格 - 使用 async/await 而非 Promise.then() - 错误处理必须包含具体错误类型 - 变量命名遵循 PR #234 的模式 ## 已知陷阱 - session 模块的 save() 是异步的,必须 await - 不要在循环中调用 API,使用批量接口 ## 成功模式 - 新增 API 端点,参考 PR #241 的错误处理 - 测试覆盖率要求参考 PR #219 的反馈Bug 修复产生长期价值添加 lint 规则预防在 Rules/AGENTS.md 中记录陷阱编写测试防止回归更新代码审查清单代码审查中提取模式反馈是否适用于未来代码?→ 记录为编码规范Agent 下次能否自动应用?→ 固化知识建立可复用工作流程## 工作流程:添加新 API endpoint 1. 先编写接口测试(参考 tests/api/example.test.ts) 2. 实现端点,遵循 src/api/users.ts 的模式 3. 添加错误处理,使用 AppError 类 4. 更新 API 文档 5. 运行完整测试套件验证值得投资的开发者体验文档:架构决策记录(ADR)、API 使用示例、已知陷阱和常见错误代码结构:清晰命名、单一职责、显式依赖反馈速度:极致快速反馈,减少无效上下文堆积快速校验脚本:组合类型检查、lint、局部测试,几秒出结果就近文档:模块目录下放 README,Agent 直接读取结构化错误信息:报错带原因、示例、参考文档六、用工程约束“驯服”Agent1. 强制执行标准(pre-commit hook)#!/bin/bash # .git/hooks/pre-commit echo "Running type check..." npm run typecheck || exit 1 echo "Running linter..." npm run lint || exit 1 echo "Running tests..." npm test || exit 1 echo "All checks passed!"2. 拦截“偷懒”行为Agent 可能尝试 git commit --no-verify 绕过检查,使用 wrapper 脚本拦截并给出明确指引:$ git commit --no-verify ERROR: Commit Rejected. GUIDANCE FOR THE AI AGENT: You have attempted to bypass the required pre-commit verification. All code must pass quality checks before it can be committed. DO NOT BYPASS THE CHECKS. YOU MUST FIX THE UNDERLYING ERRORS. ...attempt the commit again *without* the '--no-verify' flag.3. 设计 AI 友好的错误信息格式:[错误代码] 错误原因 + 具体数据 + 怎么解决 + 文档位置❌ throw new Error("Something went wrong")✅ throw new Error("[AUTH_TOKEN_EXPIRED] 令牌已过期,过期时间:2024-01-15T10:30:00Z。请调用 refreshToken() 刷新。参考文档:docs/auth.md#token-refresh")
2026年05月25日
10 阅读
0 评论
1 点赞
1
2
3
4
...
8