Lenshood

Software Developer @ThoughtWorks

1. 软件开发范式迁移

传统软件开发是"手工作坊"

在软件行业有一个被广泛接受的叙事:软件开发的本质是知识在不同形式下的传递——需求被转化为设计,再被转化为软件,软件开发是知识通过开发者发生重组的过程。

这种转化的过程知识密度很高,导致软件开发深度依赖人脑的参与,让软件开发者更像是手工业者,软件开发更像是做手艺活。当我们以手工业的视角看待以往的软件行业时,往往会很熟悉以下的现象: - 横向扩展困难: 给一个已经延迟的项目增加人手,往往使项目更加延迟(Brooks定律) - 产物高度依赖个人: 同样的功能需求,不同开发者开发的效率、质量差异巨大 - 过程可见性差: 开发者脑中的认知过程是黑箱,交付时只能看到输入(需求)和输出(代码),中间过程不可见也不可控 - 不确定性贯穿始终: 几乎没见过按计划交付的软件项目,其时间、风险、质量皆难以准确预估

《没有银弹》中总结道:软件开发的复杂性被区分为本质复杂性和偶然复杂性,偶然复杂性可以被工具降低,但本质复杂性无法消除(有意思的是距离 Brooks 在1986年提出的《没有银弹》至今已经40年,但其内核看起来仍未动摇)。 ### AI 引发的范式转移 AI 似乎也不是银弹,但整个软件开发的故事已开始被重写:偶然复杂性有望被全面解决,人类的工作面会提升到更高的抽象层级:只关注本质复杂性。

当AI能够承担编码执行、测试生成、代码审查、甚至大部分架构设计和需求分析工作时,软件开发流程就已经被全面重构:“认知契约” 将成为人与机器之间的规格图纸和双向协议,人定义意图和约束,机器反馈可行性和成本。

这恰恰是工业化的核心前提:执行层可以被标准化、可预测化、可规模化地替代,从而使管理层能够从执行中分离出来,专注于更高层级的流程设计、质量体系和持续优化。

基于此,本文想讨论的问题是: 当软件开发具备了工业化管理的前提条件时,是否有可能构建一个适用于AI时代的软件工厂管理框架? ## 2. 软件工厂管理框架

在讨论管理框架之前,我们首先要明确的是我们要管理什么?为什么要管理?

软件研发中大规模引入 AI 协作后,可以预见的有: - 产出代码的边际成本大幅降低,但相应的让人类的决策和注意力成本大幅上升 - AI 基础设施方便扩缩容,人力编排不再是难题,人类需要适应频繁上下文切换 - 与人类开发者不同,智能体没有长期记忆,领域知识共享和记忆管理十分重要 - 由于上下文窗口长度的限制,AI 存在认知漂移和产出物不一致的问题 - 智能体的工作过程完全可观测,对生产行为进行细粒度监控和优化成为可能

因此,软件工厂的管理维度应聚焦于: 1. 如何提升人类的认知和决策质量 2. 如何确保生产的一致性 3. 如何让知识和信息被充分共享 ### 三层框架 完整的软件工厂框架是一种三层结构: 生产单元层: 这一层由各种 APU(Agentic Production Unit)来组织智能体进行协作和生产,负责不同任务的 APU 会有些许差异。 这一层的目标是最大化释放AI的生产力,同时依靠生产单元和组织层面的体系来约束产出,确保生产一致性。

组织能力层: 这一层的核心目的是定义跨APU的能力和活动,例如行业知识和合规性约束,架构规则和生产规范,生产效率持续观测和改善等。 这一层本质上是聚合了企业核心的数据资产,且具有资产迭代的能力。人类和APU都依赖这些数据资产来处理自己所在层次的工作。

人类接口层: 这一层将人类的价值聚焦在方向感、判断力和创造力的活动上,例如战略输入,权衡裁决,最终确认,直觉验证等。通过人类接口层的隔离,生产细节被屏蔽掉,人类重点关注数据和决策,只在需要时才介入生产流程。

3. 框架拆解

生产单元层

生产单元层封装了实际执行工作的各类 APU,而 APU 推进生产的核心是围绕 Playbook 构建的智能体。这些智能体在 APU 中按照设定的工序将原料变为产物。

Playbook 我们先引入 Playbook 的概念定义:Playbook 是为确保智能体高质量地将输入转化为产出而设计的完整管理规范,它定义了软件工厂中不同“工种”为了完成其特定工作所依赖的要素,包括工序、约束、工装和反馈的组合。

概念 层级 说明
工序(Process) Playbook的核心 是具体的操作步骤序列
约束(Constraint) Playbook的边界 不可违反的规则和限制
工装(Fixture) Playbook的辅助工具 Skills,MCPs等
反馈(Feedback) Playbook的改进通道 数据回传和分析

软件交付 APU 以软件交付 APU为例,该单元封装了包括需求拆解,代码生产,质量保障等几个核心Playbook。

软件交付APU开始工作的前置条件是“认知契约”已就绪: - 提供需求说明书以确认交付范围,提供架构设计文档以确认技术路线 - 无论契约的形式,其内容应意图清晰、约束明确、验收标准可判定

整个APU的工作流遵循标准的迭代式开发流程: 1. 需求拆解智能体:基于输入将交付目标拆解为Story列表,过程中涉及的领域知识可以在组织知识库中搜索获取,对需求和架构的质疑会拉起产品APU和架构APU的流程进行澄清。 2. 代码生产智能体:围绕 Story 进行开发,验证,集成等工作。生产代码期间遵循架构,安全,合规等约束,涉及技术,架构层面的冲突则引入架构APU来给出决策。智能体之间互相评议代码,提出修改意见。代码通过门禁检查后直接集成并部署。 3. 质量保障智能体:对部署的功能进行验证,提供测试结论。每到阶段性里程碑时,通知人类对可运行的软件进行验收,验收期间被确认的问题和改进重新形成Story加入Backlog。

工作期间所形成的Story,设计图,代码,沟通总结,ADR,Review记录,验收记录等全部作为APU层面的上下文,可用来初始化或恢复智能体的记忆。 工作期间上报的各类相关的信息,例如Story完成的时间,讨论和对抗的次数,被反复修改的代码块,被打破的约束等,会持续被反馈上报,用来支撑数据分析。

组织能力层

组织能力层定义了跨APU的能力和活动,这些能力和活动能帮助人类和 APU 理解业务,明确边界和约束,以及对生产过程进行持续改善。

企业知识沉淀 一个金融科技公司和一个汽车制造公司,虽然都在做软件开发,但其组织能力、合规要求、质量标准是截然不同的,为了让软件工厂能开发符合企业需求的软件,智能体需要理解企业实际的业务,这是知识沉淀的目的。

以下是一些知识目录的例子: - 领域模型:业务概念、实体关系、业务规则 - 最佳实践:企业特有的架构模式和反模式 - 用户画像库:典型用户的行为模式、期望、痛点 - 竞品知识库:竞争对手的功能分析、技术架构分析 - 历史缺陷库:行业常见的缺陷模式和根因

从软件工厂的维度看,建设企业知识沉淀能力是为了服务智能体,但事实上知识沉淀的前提是数据治理。为了让智能体能找到准确的信息,同时又避免发生数据泄露等安全问题,数据治理相关的需求包括确立知识架构,提升数据质量,动态更新管理,安全与权限隔离等。

组织约束管理 由于智能体本身的非确定性特点,任由智能体发挥所产出的产物通常都会和需求大相径庭。为了确保软件工厂产出的一致性和可预测性,需要通过约束来控制整个生产过程,防止漂移。Playbook 中的“约束(Constraint)” 是其关键元素,用来定义智能体工作和产出物的边界,这些约束的来源正是组织约束管理所提供的内容。

在软件工厂管理框架下,需要定义约束应包含以下几种: 1. 架构和技术约束: - 允许/禁止使用的技术、框架、第三方服务 - 组织级的架构偏好、禁区、演进方向 2. 安全和合规约束: - 数据保护、访问控制、加密要求 - 法规、标准、审计规范 3. 成本和交付约束: - Token预算、基础设施预算、其他预算 - Definition of Done,交付时间,项目节点

有约束就必须要确保约束被遵守,约束分为指导性和强制性: - 指导性约束:基于Playbook中的约束,指导智能体的生产,同时智能体之间也相互进行对抗检查 - 强制性约束:通过linter,checker,pipeline 等工具,强制对产物进行门禁检查

持续改善优化 由于智能体工作过程的高透明度,让软件工厂能以比人类团队细致的多的层面对生产过程进行观察。这种观察能在生产一致性和质量提升层面提供巨大价值。

改善的前提是反馈,软件工厂管理框架下的反馈可以分为如下几个维度: 1. 任务级反馈,每完成一批相关Tasks后分析相关数据: - 返工率突增 → 检查是否规格有歧义 - 某类任务反复失败 → 检查需求和方案 - 某类 Token 消耗变化 → 检查Playbook和模型 2. APU 级反馈,对于给 APU 实施的改进: - 分析哪些改进带来了质量提升或缺陷率降低 - 分析哪些改进没有效果甚至有副作用 - 工厂级反馈,定期回顾: - 哪些类型的需求交付效率最高?为什么? - 哪些类型的效率最低?Playbook能否改进?

可定义以下关键指标来评估软件工厂生产的一致性和质量:

维度 指标 含义
功能一致性 功能行为等价率 两组不同智能体对同一需求的实现,在相同输入下输出是否一致
效率一致性 成本等价率 两组实现在相同模型和负载下的token消耗和基础设施费用是否接近
缺陷密度 千行代码缺陷率 AI生成代码的缺陷密度趋势
第一次通过率 无需返工的比例 代码生成后直接通过所有质量门禁的比例

人类接口层

回到文章开头的判断,我们期望 AI 能够帮助我们解决掉软件开发中的偶然复杂性,让人类能专注于本质复杂性的解决,因此人类接口层的目的就是为了给人类提供一个管理视角,提供人与AI在不同工作面之间的交互接口。

需要人类参与的场景包括:

参与类型 触发条件 人类角色
战略输入 项目启动、方向调整 定义Why,设定约束和优先级
权衡裁决 对抗无法收敛 在多个方案中选择,承担决策责任
最终确认 质量门禁通过前 确认交付物符合预期
直觉验证 探索性测试阶段 发现AI无法察觉的体验和感受问题

APU在生产过程中遇到需要人类帮助的情况应通知人类。人类应当及时响应通知以避免生产停滞,但通知本身也是对人类的干扰。对于通知,应遵循如下原则: 最小打扰原则: 只有真正需要人类决策的问题才推送给人类。AI内部能解决的对抗不打扰人类。 充分上下文原则: 推送给人类的裁决请求必须包含争议背景、各方主张和证据、不同选择的预测后果、AI的倾向性建议。 可追溯原则: 人类的每个决策都被记录,用于事后审计和决策模式学习。 渐进授权原则: 初期人类频繁参与裁决,中期AI根据人类裁决历史减少推送,长期人类只关注战略方向和异常情况。但关键决策始终保留人类裁决权。

4. 下一步

在前文提到的软件工厂管理框架中,对框架的三个层次尚未深入。后续将逐一讨论设计考量:

  • 生产单元层:APU 生命周期、Playbook 工序设计与失败恢复、多 APU 协调协议。
  • 组织能力层:知识沉淀的数据治理、约束体系的分层执行、持续改善闭环。
  • 人类接口层:认知契约的生命周期、通知与裁决机制、渐进授权的边界。

There was a very interesting lab in the course of Unsupervised Learning, Recommenders, Reinforcement Learning taught by Prof. Andrew Ng, which trains a machine learning model to make sure the lunar lander land in a pre-defined range of area. In the course, Andrew also mentioned one of his team's great work that using machine learning model to drive a toy helicopter flying inverted in the real word. That's amazing, the work they have done has been published: Autonomous Helicopter Aerobatics through Apprenticeship Learning.

The entire lab is based on the Gymnasium, which provides experiential environments for reinforcement learning. After finished the lab, I noticed the Gymnasium is quite flexible and easy to extend, making it possible to play the env in some fancy ways beyond the default configuration.

Read more »

自各类计算机程序开始被编写、运行开始,我们就一直想通过各种方式来了解它的执行过程和状态从而判断计算机程序运行的行为和效率。作为被使用最广泛的操作系统,Linux 经过多年发展,拥有了各类工具和组件来实现对用户程序以及内核程序的追踪,这些组件组成了 Linux 的追踪(Tracing)系统,它的魔力令人着迷。

本文将从最基础也是最灵活的 Kprobes(Kernel Probes) 入手,了解 Linux Tracing 系统的设计(本文基于 linux kernel v6.15.4)。

Read more »

Based on disk and file management, now we are able to store the user space program on the disk, and let them run after kernel started. But before that, there is still a topic we haven't covered: how does xv6 jump from kernel space to user space?

After all, the content we talked about in previous chapters is only limited in the supervisor level, even machine level, where the code has full control of hardware. However, the user space program cannot be granted such huge scope of control, then we should know how to jump from kernel space to user space, so that we could provide a safer environment for the user program.

In this chapter, we are going to find it out.

Read more »

With all of the content in the previous chapters, we have known how to initialize and run a process, but before the kernel runs the first line of a process's code, a question still remained, how does my user program store in the disk and how is it loaded?

In this chapter, we are going to discover the disk management and file system in the xv6, we'll see the persistence stack from the top to the bottom, let's get started!

Read more »

In this chapter, we are going to explore the cpu virtualization, also known as process, in the xv6.

I'm really excited about writing this chapter, because process is one of the fundamental concepts of the operating system. Process is very useful for multiple tasks, and in the design wise, its abstraction is also very elegant.

Read more »

We have learnt how to setup risc-v in rust, and also initialized risc-v to be able to print format strings, in this chapter we are taking the first look of the OS kernel, and will try to figure out the memory management in xv6.

Read more »

With the help of the previous article, right now we have a good foundation for running rust on risc-v platform.

In the second episode, we are going to jump into some real code of xv6, and take care of the initialize from machine level to supervisor level, and finally, make the printf!() macro available in our code!

Read more »

Xv6 is one of the best operating systems for teaching. It’s a great way to learn about how an OS works with basic functions and few lines of code.

Originally, xv6 was written in C, which is awesome for students to get hands-on experience with such a classic programming language. But now that Rust is gaining traction—especially since rust-for-linux is becoming a part of the main line Linux—wouldn’t it be fun to run xv6 using Rust?

As a perfect way to kill time, I have migrated most of xv6 from C to Rust. You can check it out here. During this migration process, I encountered many sorts of issues and tricky stuff, nothing brings me more satisfaction than successfully resolving a problem!

Therefore, I believe it would be cool for me to share my experiences through a series of articles detailing how I did this, complete with a more structured approach and clear procedures.

All right, let's get started...

Read more »
0%