契约化多端架构:基于领域模型的Harness实践(上)

发布时间:2026/7/27 5:54:26
契约化多端架构:基于领域模型的Harness实践(上) 契约化多端架构基于领域模型的Harness实践上本文为《契约化多端架构基于领域模型的Harness实践》系列第 1 篇共 3 篇分为上中下三篇建议连续阅读。作为一名老登前端开发这两年深刻感受到大模型时代 AI 对软件工程带来的冲击——也被近年来 AI 各种范式大潮冲击了一遍又一遍。一路从提示词工程再到上下文工程、Vibe Coding再到本篇要介绍的 Harness 工程化一路在思考什么样的 AI 工程化方案才是符合 WeTV腾讯视频海外平台当下 Web 项目的这里抛砖引玉和大家一起来探讨。 【为了将每个细节讲到味篇幅较长文章有问题的地方欢迎指出大佬们不吝点赞收藏】01 我们是怎么开始吃 AI的说实话2025 年初那会儿AI 热潮铺天盖地——朋友圈、技术社区、各种大会全是不用 AI 就被淘汰的论调。作为老登说实话有点焦虑。正好那段时间我们接连有两个从 0 到 1 的项目Linux TV 改版、Roku TV 立项。按以往经验这种新项目意味着大量重复劳动——搭建项目框架、写基础组件、对接 API、写测试用例... 总得有个起点吧当时就想要不拿 AI 试试1.1 AI 辅助编程的那点历史回过头看这事儿其实折腾了很久。图灵 1950 年的那篇论文其实不是在讨论AI 写代码而是提了个判据如果机器能通过对话骗过人类就算有智能——这个设想埋下了后来用自然语言编程的种子。70 年代有人研究自动编程Automatic Programming试图从形式化规约推导程序当然没成。80 年代专家系统火过一阵用 IF-THEN 规则做代码审查但规则维护成本太高最后也没落地。2000 年代统计机器学习入场但还是没改变开发者的日常。真正的转折是 2017 年的 Transformer就是那篇 Attention is All You Need然后 2021 年 Copilot 上线AI 辅助编程才算真正进入主流。但进入主流和真正好用之间还有很大的距离——这就是为什么我们从提示词工程一路走到 Harness 工程化。1.2 没有 AI 之前我们其实已经做了很多工程化面对 Web 侧这种多平台产品矩阵——Linux TV、V 站、M 站、Roku TV——我们为了提升研效早就开始工程化了tencent/wetv-kernel核心逻辑包平台无关上报/账号/播放器 SDK基础能力收敛规范检测集、目录范式、单测范式多年踩坑沉淀没有 AI 之前我们靠人肉传承——有了 AI 之后第一反应是怎么让 AI 也懂这些范式1.2.1 上下文腐化memory 不是万能的刚开始用 AI每次写提示词都想能不能把这些范式和复用场景直接给到 AI但很快发现——上下文工程虽然有 memory但体量一大就会腐化规范冲突AI 生成的代码经常忘记我们的特殊规则开发范式丢失聊着聊着AI 就忘了Page / Module / API这些开发范式复用场景不识别明明有现成 SDKAI 还是给你重新写冗余逻辑这时候我们才意识到memory 不是万能的。1.2.2 从人肉传承到契约化规范意识到 memory 不行之后我们开始想既然 AI 的上下文会腐化那就把规范外置——不让 AI 靠记忆而是靠查契约。具体来说就是把我们之前沉淀的那些东西——规范检测集、目录范式、单测范式、开发范式模型——从人读的文档变成机器可读的契约。这一步其实就是从上下文工程走向Harness 工程化的关键转折点。02 我们需要什么样的Harness2.1 用契约代替猜以前让 AI 写代码它得先把项目扒一遍然后靠猜来理解你的意图。项目一大该读的没读到不该读的干扰一堆——这就是上下文腐化。Harness 的做法是提前定义好领域模型——用 JSON 把业务概念写清楚页面有哪些、组件有哪些、接口有哪些、数据结构是什么。AI 直接读这个就行不用去扒源码。关键是这份领域模型跟平台没关系各端共用同一份。运行 /project:init 的时候AI 会根据_DETECTION_HINTS自动扫描当前项目生成 domain-mapping.json 映射文件把通用契约映射到各端具体实现{通用契约定义是什么映射文件定义在哪、怎么实现。2.2 多端差异用映射解决业务逻辑是通的但各端实现方式不一样——TV 用焦点驱动Web 用鼠标移动端用触摸。Harness 用_PLATFORM_SPECIFIC标注各端差异。AI 生成代码时先读通用契约再读映射文件最后结合项目依赖库生成符合该端技术栈的代码。2.3 把整个流程串起来完整工作流/project:init → 需求分析 → 红队验证 → 验收标准 → 方案设计 → 需求开发 → 测试验证 → 观测回收初始化AI 探测项目结构生成映射文件和配置需求分析AI 读需求拆成任务清单让你确认——避免理解偏差红队验证AI 自己当对手找漏洞——没网络怎么办数据为空怎么展示验收标准生成 TDD 契约文件开发完了对着验收需求开发AI 创建 Team 分工——reader 读代码、planner 做方案、executor 写代码、reviewer 做 ReviewL0-L4 验证五层质量门禁自动拦截不合格代码观测回收记录 Token 消耗、首次通过率、返工次数2.4 用建房子理论类比图03 我们的 harness 如何编排设计早期我们让 AI 一口气干完读需求 → 拆任务 → 写代码 → 跑测试结果是上下文一多就乱改一处漏一片。后来想通了把开发流程拆成角色每个人干自己的活上下文隔离互不干扰。Command、Skill、Agent 到底啥区别刚接触的人容易搞混其实很简单Command 是你跑的那个命令。比如 /web-agent-new它就是个入口负责解析参数、拉 TAPD、串步骤。它不干活它是调度员类似我们以前开发脚手架 Cli的命令入口。Skill 是专项工具。比如 requirement-breakdown 专门拆需求d2c 专门把 Figma 转代码。它被 command 调用只做一件事做完就返回。好处是复用方便换个项目不用改。Agent 是带角色的执行者。比如 code-analyzer 是代码阅读者code-planner 是方案设计者。它有角色设定会在独立上下文里自主决策。一句话区分Command 是入口Skill 是工具Agent 是角色。跟业界比我们做了哪些优化工具怎么干活的多角色懂业务Copilot / Cursor一个模型聊到底✗✗Devin一个 agent 自己干✗✗OpenHands多 agent 协作✓得自己配✗我们的HarnessCommand Skill Agent Team✓流程里固化了✓领域模型注入最大的区别Copilot / Cursor 你得把需求说清楚它自己去扒代码猜意图。Harness 提前把业务概念结构化领域模型AI 直接读不用猜。一个真实踩的坑最早设计的时候我们让主面板把需求管理也当子 agent 启动——结果卡死了。原因是子 agent 弹的确认框出现在子 agent 面板主面板看不到就一直等死锁。后来定了一条铁律所有用户交互必须在主面板子 agent 只执行不交互。这个坑修了两轮才彻底闭合。现在的流程里每个检查点都是主面板直接弹 ask_followup_question子 agent 返回结果后主面板再决定下一步。有了上面的 harness顶层设定接下来我将核心内容拆开讲把这套东西的设计细节摊开说。主要分三块先是领域模型。这是整套 Harness 的核心——把业务概念结构化让 AI 能读懂。四个模型文件pages、ui-modules、api-layer、data-layer分别管什么、怎么配合_DETECTION_HINTS和_PLATFORM_SPECIFIC这两个关键设计是怎么工作的都会说清楚。然后是完整工作流。从你跑 /project:init command命令开始到需求分析、需求开发、L0-L4 验证再到最后的观测回收每一步具体做什么、产出什么、怎么保证质量都会拆开讲。最后是管理机制。这套东西做好了得让团队真正用起来。各端项目怎么接入、怎么升级、文件怎么管理、版本怎么迭代这些是落地时要解决的。04 领域模型设计前面说了领域模型是一份业务说明书——用 JSON 写清楚业务里有哪些页面、哪些组件、哪些接口、哪些数据。AI 读这份说明书就能理解业务不用去扒源码。4.1 四层架构四层从上到下是依赖关系页面层pages.json实际跑起来是这样用户访问首页 → AI 识别 HomePagepages.json→ 分解出需要 HeaderModule PosterListModuleui-modules.json→ 识别数据依赖 ChannelListDatadata-layer.json→ 生成调用 ChannelListAPIapi-layer.json的代码 → 输出各端实现。4.2_DETECTION_HINTS让 AI 自己找文件领域模型是通用业务概念但各端具体实现文件路径不一样。首页在 Web 端是 pages/index.tsxLinux TV 端是 pages/home.tsxRoku 端是 components/HomeScene.brs。以前的做法是在领域模型里硬编码各端路径但项目重构后路径变了模型就错了。现在的做法是用_DETECTION_HINTS给 AI 提供探测提示让 AI 在 /project-init 时自动扫描项目生成 domain-mapping.json 映射文件。看实际例子{跑 /project-init 时 AI 会扫描项目目录生成{4.3_PLATFORM_SPECIFIC处理各端差异文件在哪解决了还有个问题各端的 props 不一样。PosterCard 组件通用定义里只说这是一个可点击的海报卡片但 TV 端需要 focusKey、onFocus焦点驱动Web 端需要 onMouseEnter鼠标悬停。做法是在通用定义里用_PLATFORM_PROP_VARIATIONS标注各端差异{AI 生成代码时先读通用契约再读 domain-mapping.json 里的平台映射最后结合项目依赖库生成符合该端技术栈的代码。4.4 glossary.json统一业务语言业务里有很多专有名词不同人叫法不一样。比如视频 ID有人叫 vid有人叫 videoId有人叫 contentId。AI 读代码的时候就会混乱。用 glossary.json 统一业务语言{AI 读领域模型之前先读 glossary.json建立统一的业务语言理解。