如何用 Kimi K3 搭建公司操作系统(构建者指南)

How to Build a Company OS using Kimi K3 (Builder's Guide)

中文译文 · 38k 字

一句话摘要

用 Kimi K3 搭建公司级操作系统的方法

如何用 Kimi K3 构建一家公司的 OS(构建者指南) 2026 年 7 月 21 日 · 33 分钟阅读 · 查看原文 ↗ AI 设计 Claude 营销 这是对 Kimi K3 的完整 A–Z 拆解——它是什么,以及如何只用 AI Agent 运行你的整个业务。 这会彻底改变你和 Kimi 一起工作的方式。 > TLDR;如果你不想读一篇 4480 字的文章,这里是你可以交给你的 Agent 的 GitHub 仓库。https://github.com/codejunkie99/meridian-company-os > > 趁没忘,先把这些构建收藏起来。 前言 每个公司 OS 都需要的元素,从零推导出来,附上构建每个元素的提示词。学会原理,拿走提示词,构建你自己的。 我建了一个(meridian-company-os,MIT 许可),它是这篇指南的参考,而不是重点。重点是它底下的九个元素,因为它们是任何你将来会构建的公司 OS 底下的元素。 趁没忘,先把这 9 个构建收藏起来。 Command 驾驶舱:来自 BUILD 5 的操盘手界面,以及九个元素加起来的样子。 引言 你在用一个聊天窗口和一厢情愿来编排 Agent。一个能花钱、能交付代码、能雇佣其他 Agent 的 Agent,就是一家公司,而你在用一个搜索框的工具在运营那家公司。这篇指南修复这一点。 这里是 2026 年 7 月的现状。一个 worker 层的编码 Agent,会用不到一分钟、几美分的成本,生成任何你能用一段话描述的文件。 它也会很开心地花掉你的预算、自己解决自己的审批、并在刷新时忘掉一切,因为没人建墙。模型变便宜了。围绕它的运营结构没有被建起来。 一个公司 OS 不是一个更好看的聊天 UI。它是六个问题的答案,且随时都是: 谁拥有什么 哪些目标重要 什么被卡住了 钱烧得多快 什么在等你的"是" 你不在的时候发生了什么 六个都答上,你就有公司 OS。答得少,你有的就是 demo。 这不是某个人的哲学,也不是对我仓库的逐行讲解。它是必需的要素,每个要素都配一个你交给编码 Agent 的通用化提示词、它为我产出的参考实现、以及一个证明该要素存在的检查。 我的技术栈是 React 19 + TypeScript + Vite。你的可以是任何东西。要素不变。 整件事实际是怎么建出来的,这样你能复制方法而不只是复制输出: 运行时:每个文件都由 Kimi K3 通过 Kimi Code CLI 生成(~/.kimi-code/bin/kimi,kimi login 一次)。 循环,九次:写提示词,通过 kimi -p 管道进去,跑检查。文件错了?修提示词再重新生成,永远不要手工修文件。 分层:worker 层的 K3 处理了九个构建中的八个;一个(reducer)被升级到了前沿模型。 Skills,全程安装:Superpowers(obra/superpowers)用于计划和审阅纪律;Context7(upstash/context7)用于版本精确的 React 19 和 Vite 6 文档,这样 K3 就不再幻觉旧 API。 这就是整套工具链。你会在下面每个构建里看到它在运作。 你最后会拥有的,逐元素: 一套词汇:每个公司 OS 都必须达成一致的、带类型的名词(BUILD 0) 一个世界:一个正运营中的种子公司,让控制台永不空屏(BUILD 1) 单一事实来源:一个 store、一个 reducer,没有视图拥有状态(BUILD 2) 一个心跳:公司按自己的时钟走,而不是你的(BUILD 3) 一份记忆:刷新后仍然存活的状态(BUILD 4) 一个操盘手界面:一个回答"发生了什么、需要我吗、我该做什么"的屏幕(BUILD 5) 一道门:钱和权力排队等你的"是"(BUILD 6) 一条命令行:输入的命令在任何模型被调用之前就变成真实动作(BUILD 7) 一个真实运行时:一个实际接进来的 Agent,关在栅栏后面(BUILD 8) 这是给谁的:任何有终端、有编码 Agent、有意愿一次运行不止一个 Agent 的人。你不复制我的文件。你拿走原理和提示词,让你的 Agent 写你的文件。 怎么读:按顺序,做检查。每个元素都假设它前面的那些。检查是证明该元素存在的证据;跳过它,你就是在流言上堆楼。 一切底下的原理。三条,而接下来九个构建里的每个设计决策,都是其中一条的实例: 公司是状态。不是氛围,不是聊天记录。一棵带类型的事实树,而每个屏幕都是一扇看向它的窗,永远不是它的来源。 权力流经门。任何花钱、雇佣、交付或解雇的东西,都排队等一个明确的"是"。没有门,就没有公司,只有一个带 UI 的漏洞。 没写下来的就没有发生。每一次心跳、每一个决策、每一块钱,都落进一个只追加的日志。日志是公司对自身的记忆。 从这里开始,不再写小作文。 前置条件 node --version # v20+;任何现代运行时都行,这是参考用的 npm --version # 随 node 一起发布 # 生成每个文件的编码 Agent: ls ~/.kimi-code/bin/kimi # kimi code cli 已安装 kimi login # 一次;本地存储 oauth 凭据 # 在第一条提示词之前安装在 kimi code 里的 skills: # superpowers (github.com/obra/superpowers) 计划 + 审阅纪律 # context7 (github.com/upstash/context7) 新鲜、版本精确的文档 把运行时钉死一次:Kimi K3 是生成参考文件的 worker 层。跑一个不同的模型,你会得到不同的文件,这没关系,因为你是在构建你的,不是我的。你保持不变的是提示词和检查。 为什么是 Kimi K3 Kimi K3 一瞥:发布规格和公开基准排名,风格化以匹配控制台。 运行时选择,快速版。不是炒作,只是权衡。 它是什么 Moonshot AI 的开源权重模型,2026 年 7 月 16 日发布。 2.8T 稀疏 MoE(每个 token 896 个专家中激活 16 个),1M 上下文,原生视觉。 第一个开源的 3T 级模型,迄今最大的开源权重发布。 完整权重 7 月 27 日落地;在那之前仅托管。 它输在哪里(说实话) 在 Moonshot 自己的发布表上,Fable 5 赢 35 项中的 22 项;K3 赢 12 项。 Intelligence Index 上 189 个里排第 4(约 57,对比 Fable 5 的 60 和 GPT-5.6 Sol 的 59)。 FrontierMath Tier 4 上低于 40%,而闭源前沿接近 90。 幻觉率约 51%,所以让一个验证器留在环里。 它赢在哪里(正是这个工作负载) 盲测 Frontend Code Arena 第 1(1,679,领先 Fable 5)。 Terminal-Bench 2.1 上 88.3%;领跑 SWE Marathon 和长程 agentic 编码。 那个"在多步工具调用会话里存活而不脱轨"的技能——它成就或毁掉一个 agent 运行时。 为什么选它 开源权重:权重一发布就自托管,拥有你的运行时。 便宜:$0.30/M 缓存命中输入、$15/M 输出,比 Fable 5 低约 70%。 你整天跑 Agent,而你不能从一个供应商那里租你的运行时、数据和成本曲线。 在 agentic 编码上足够前沿、开源、而且便宜 70%,胜过租来但你没法自己跑的基准分。 地图 九个元素,以及它们在参考实现里变成的文件。你的文件名会不同。你的元素不会。 any-company-os/ 脚手架 BUILD(本节):运行时 + 严格类型、最小依赖 领域模型 BUILD 0:名词 -> src/lib/types.ts 种子世界 BUILD 1:永不空启动 -> src/lib/seed.ts, skills.ts 事实来源 BUILD 2:一个 store -> src/lib/store.tsx 心跳 BUILD 3:2.6 秒 tick -> src/lib/sim.ts 记忆 BUILD 4:刷新存活 -> store.tsx 里的持久化 操盘手界面 BUILD 5:驾驶舱 -> src/App.tsx, views/Command.tsx 门 BUILD 6:审批收件箱 -> views/Approvals.tsx 命令行 BUILD 7:和它对话 -> views/KimiSpace.tsx 真实运行时 BUILD 8:带栅栏的 agent -> server/kimiBridge.ts, vite.config.ts 这篇指南里的全部十个提示词,也作为可运行文件随 prompts/ 一起发布,每个元素一个,这样 kimi -p "$(cat prompts/00-scaffold.md)" 开箱即用。 首先是脚手架。原理:更少的依赖,更少的谎言。一个公司 OS 的唯一工作是可信的状态,而每一个依赖都是别人的状态,你现在不得不信任它。 提示词,通过 kimi -p: im building a company os. one console to run a whole company of humans and ai agents from. pick me a lean setup: typed language, fast dev loop, and as close to zero runtime deps as you can get away with. set up the scaffold and tell me what you put in and why. anything you cant justify in one line, rip out. K3 为参考产出的:React 19 + TypeScript 5.8 strict + Vite 6,以及 React 之外恰好一个运行时依赖(lucide-react 做图标)。脚本:dev、build(tsc -b && vite build)、preview。 Context7 在这里已经很重要了:没有它,K3 脚手架出 React 18 的模式;有它,配置第一次就出来 Vite 6 原生。 检查:安装并类型检查,退出码 0。数你的运行时依赖;如果你不能一行一个理由地解释每一个,这个构建就没做完。 npm install && npx tsc --noEmit; echo "exit: $?" BUILD 0:词汇 为什么,一行:公司是状态,所以在任何行为存在之前,公司运行所依赖的每一个名词,都必须有恰好一个带类型的定义。 第一性原理。问,任何由人和 Agent 组成的公司里,什么不可约地存在,你会得到七个名词: 一个干活、花钱的行动者(actor) 一个说"为什么"的目标(goal) 一个说"是什么"的任务,带状态和负责人(task) 一个等权力的决策(一个审批 approval) 一个花费单位(账本 ledger) 一个说"它发生了"的事件(一行日志 log line) 一个把它们全装起来的容器(公司 company) 每个公司 OS 都是这七个名词加观点。先把名词定类型,观点就保持诚实。 通用化提示词。注意它命名了名词,以及对每个名词要问的两个问题,而不提我的技术栈: ok before any logic i want the nouns. if im running a company of humans and agents, what are the things that have to exist? actor, goal, task, approval, money spent, a line in the log, the company holding it all. model all of it in types, no behavior. for each noun ask two questions: what does the operator need to see, and what does the system need to enforce? statuses are closed unions not strings. money and tokens are numbers not vibes. and if two of my nouns are secretly the same thing, call it out, dont let me ship a mess. 参考实现。K3 产出了 src/lib/types.ts,416 行,零逻辑。承载整个系统的形状,以及每个里面可见的强制问题: export type AgentStatus = "working" | "idle" | "paused" | "blocked" | "offline"; export interface Agent { id: ID; companyId: ID; name: string; title: string; department: string; kind: "ai" | "human"; runtime?: string; model?: string; managerId?: ID; status: AgentStatus; heartbeat: string; monthlyBudget: number; spent: number; // 强制:花费有上限 successRate: number; tasksCompleted: number; skills: string[]; color: string; lastHeartbeat?: number; } export type ApprovalType = "hire" | "spend" | "override" | "publish" | "terminate"; export interface Approval { id: ID; companyId: ID; type: ApprovalType; title: string; rationale: string; requestedBy: ID; amount?: number; status: "pending" | "approved" | "rejected"; // 强制:三个状态,没有第四个 checks: PolicyCheck[]; // 机器陈述它的理由 decidedAt?: number; decidedBy?: string; } export interface ActivityEvent { id: ID; companyId: ID; ts: number; actorId: ID; kind: "heartbeat" | "task" | "delegation" | "spend" | "approval" | "governance" | "goal" | "system"; message: string; amount?: number; // 没写下来的就没有发生 } Kimi 那一轮怎么走的:一次 kimi -p 调用,整个文件一次成型。 Superpowers skill 在这里挣到了它的位置。它的审阅纪律让 K3 追加了一条"你的两个名词重叠了"的注释:一次委托(delegation)和一次任务指派(task assignment)几乎就是同一个名词,被解析为 Task 上的一个 delegatedBy 字段,而不是一个新的 interface。那是提示词最后一句话在做实事。 检查:类型检查通过,零 any。然后做名词审计:grep 你的 interfaces,确认七个名词里每个都恰好有一个家。 npx tsc --noEmit && grep -c "^export interface" src/lib/types.ts BUILD 1:世界 为什么,一行:你没法学会运营一家空公司,所以 OS 必须启动进一个已经运营中的世界。 第一性原理。一个操盘手界面通过识别来教学:你看到一个超预算的 Agent、一个被卡住的任务、一个待批的雇佣,你就学会了控件是干什么的。一个空状态什么也教不了,而且它还藏渲染 bug(一个空列和一个坏列看起来一模一样)。 所以任何公司 OS 都需要一个确定性的种子世界:每次启动都是同一个世界,每个状态都出现,每道门都已经握住一个决策。 通用化提示词: take the types we just wrote and fake me a whole company thats already running, so the app has stuff on screen the second it boots. who works here? give em real titles, who reports to who, budgets theyve half burned through, hit rates. whats being worked on rn, whats stuck? what decisions are sitting in my inbox waiting on a yes? every status in the model shows up at least once. make it feel like i walked into a live company at 2pm on a tuesday, not an empty template. no random(), same world every boot. 参考实现。K3 产出了两个文件。 seed.ts(约 600 行夹具): 两家公司,每家六个以上 Agent,带汇报线和部分花掉的预算 一棵 mission-到-公司-到-团队的目标树 覆盖全部六种状态的任务 带策略检查结果的待批审批 以及 skills.ts,一个可安装能力包的注册表,注明它们的真实来源: export const SKILLS: Skill[] = [ s("superpowers", "Superpowers", "obra/superpowers", "https://github.com/obra/superpowers", "developer", "Battle-tested workflow superpowers: TDD, debugging, planning, and review discipline."), s("context7", "Context7", "upstash/context7", "https://github.com/upstash/context7", "developer", "Pulls fresh, version-accurate library docs into the agent's context window."), // ...design, marketing, social, finance, operations, legal packs ]; 有一个值得注意的循环。在生成这个文件时,跑在我 Kimi Code CLI 里的那两个 skills,正是这个文件定义的注册表里的前两个条目。 你正在构建的 OS 给它的 Agent 装 skills 的方式,和你刚刚给构建它的 Agent 装 skills 的方式,一模一样。方法即产品。 这里需要一次重新生成。K3 的第一遍把所有任务都放进了 in_progress,这违反了"每个状态至少一次"那一行。修复不是去改 seed;是把那一行加进提示词,然后 kimi -p 再来一遍。修提示词,永远别修文件。 检查:启动两次,diff 世界。确定性意味着完全一致。 node -e "const {seedState}=await import('./src/lib/seed.ts'); \ console.log(Object.keys(seedState().tasks).length)" # 每次运行,同样的数量 BUILD 2:事实来源 为什么,一行:公司是状态,所以状态只能在一个地方改变,其他一切都是窗。 第一性原理。每一个多 Agent 仪表盘的失败模式都是一样的:五个组件各持有一份真相的副本,漂移。解药是结构性的,不是纪律性的。 一个 store。一个 reducer,一个从状态加动作到状态的纯函数。视图读;视图派发;视图从不变异。 做到这一点,后面每一个元素(日志、门、心跳)都变成一个 reducer case,而不是一个架构决策。 通用化提示词: every screen should just be a window onto one source of truth, not its own little pile of state everywhere. build me that source. if the store is the ONLY place state can change, what shape is it, and how does a button ask it to change something without reaching in and mutating junk? one pure reducer, one action union, selectors for the common reads. keep it so i can bolt on new actions later without rewriting the world. build it. then tell me the one case youd bet money i break first. 参考实现。src/lib/store.tsx,约 1,400 行:StoreProvider、返回 { state, dispatch } 的 useStore()、覆盖 Action 联合每一个成员的 reducer,以及 selectors(companyAgents、companyTasks、companyApprovals、agentName)。这个同时承载原理二和原理三的 case: case "decideApproval": { const a = s.approvals[action.id]; if (!a || a.status !== "pending") return s; // 不允许二次决策 const decided = { ...a, status: action.approve ? "approved" : "rejected", decidedAt: Date.now(), decidedBy: "you" } as const; return { ...s, approvals: { ...s.approvals, [a.id]: decided }, activity: [{ id: crypto.randomUUID(), companyId: a.companyId, ts: Date.now(), actorId: "you", kind: "governance", message: `${action.approve ? "Approved" : "Rejected"}: ${a.title}` }, ...s.activity], // 决策被写下来了 }; } Kimi 那一轮怎么走的:这是 K3 卡住的唯一一个构建。两次 worker 层尝试产出的 reducer,在三处变异了嵌套对象,两处都被检查抓住,而不是靠读 diff。这个构建被升级到前沿模型,提示词一字不变,第一遍就干净。 这就是实践中的分层规则:K3 是默认 worker,前沿模型保留给那个"纯性即全部要点"的唯一构建。问它会押钱我第一个搞坏哪个 case 时,它点名了"决策一个已经决策过的审批",于是就有了 status !== "pending" 那个守卫。 检查:用 grep 做穷尽性。联合里每个 action 都有一个 case,否则它就是一个等着被按的死按钮。 grep -c 'case "' src/lib/store.tsx # >= 你的 Action 联合里成员的数量 npx tsc --noEmit BUILD 3:心跳 为什么,一行:真实公司在你不看的时候也在动,所以 OS 必须按自己的时钟 tick,否则它就是在用一个谎言训练你。 第一性原理。Agent 持续花钱、持续进展;你的注意力是离散的。一个只有当你点击才变化的控制台,会教你"两次点击之间什么都没发生",这恰好是错的,也恰好是昂贵的。 所以任何公司 OS 都需要一个心跳:一个小的、有界的、在计时器上触发的纯状态转移。有界是关键那个词。一个无界的 tick 是失控;一个有界的 tick 花几分钱的模拟花费,并证明管道是通的。 通用化提示词: i want this thing alive even with zero real agents plugged in, so when i open it the numbers are already moving. picture one heartbeat of a running company, like 2 seconds of it. what changes? a bit of money burns, some work creeps forward, a log line drops. make that one step a pure state -> state function so i can fire it on a timer and trust it never corrupts anything. cap everything: spend per tick, progress per tick, log length. whats the smallest believable amount of movement, and how do i stop it running away? build the tick and defend the numbers you picked. 参考实现。src/lib/sim.ts:runTick(state): State,由 provider 里唯一的一个 interval 每 2,600 毫秒触发一次。每次 tick:一个工作中的 Agent 累积 $0.40 到 $3.04,一个打开的任务获得 3 到 10 个百分点的进展,一行心跳落地,日志封顶 500 条、最新的在前。 被要求为这些数字辩护时,K3 的答案作为设计存活了下来:一个刚好肉眼可见的 tick(2.6 秒)、花费小到一小时模拟只花零钱、一个日志封顶让内存无法爬升。 export const TICK_MS = 2600; export function runTick(s: State): State { const tickN = Math.floor(Date.now() / 1000); const agents = Object.values(s.agents).filter((a) => a.status === "working"); if (agents.length === 0) return s; const actor = pick(agents, tickN); const spend = +(0.4 + (tickN % 13) * 0.22).toFixed(2); // $0.40..$3.04,有界 // ...推进一个任务,追加事件... return { ...s, activity: [...events, ...s.activity].slice(0, 500) }; // 封顶 } 而且恰好一个 interval,由一次范围限定在单个 hook 的后续 kimi -p 编辑接上:当 simRunning 时,每 TICK_MS 派发 {type:"tick"},清理时清除,interval 不在任何别处。 检查:盯着 feed 看 30 秒;行大约每 2.6 秒落一条。暂停;它冻结。恢复;它动了。行到达得比 2.6 秒快,意味着两个 interval,而两个 interval 意味着某个组件在干 provider 的活。 BUILD 4:记忆 为什么,一行:一家刷新就忘掉自己的公司是 demo,而 demo 和系统之间的分界线,是一次重载。 第一性原理。把所有状态拆成两种,设计自己就写出来了。领域状态(谁在这里工作、花了什么、决定了什么)是公司;它必须存活。会话状态(你之前在哪个屏幕、一个打开的模态框、一个 toast)是你的访问;持久化它是一个 bug。 所以:一个领域切片的允许清单快照,在真实编辑之后用防抖写入,启动时恢复。以及一条顺序法则:永远不要在恢复完成之前写入,否则你会用一张空白覆盖掉公司。 通用化提示词: rn a refresh nukes the whole company back to seed. thats a demo not a system. i want state to survive reload. think about what actually deserves to be saved vs what doesnt: whats real company data vs whats just where i happened to be clicking? allowlist the real stuff, explicitly exclude the session junk. whats the fail case if i save at the wrong second, and how do i make sure i never overwrite good data with a half-loaded blank? build the save + restore path and warn me about the ordering trap before i step in it. 参考实现。一个 hydrate action,一个 persistable() 允许清单(companies、agents、goals、tasks、approvals、runs、activity、customSkills、activeCompanyId),一个 400 毫秒防抖写入,以及提示词里点名的那个陷阱,由一行守护: useEffect(() => { if (!state.hydrated) return; // 顺序法则:永远不要在恢复之前写 const id = window.setTimeout(() => { window.localStorage.setItem(SNAPSHOT_KEY, JSON.stringify(persistable(state))); }, 400); return () => window.clearTimeout(id); }, [state]); Kimi 那一轮怎么走的:一次对现有 store 的 kimi -p 编辑,而不是一个新文件。提示词里的警告子句,正是 hydrated 那个门存在的理由。 被要求在踩进去之前先点名陷阱,K3 点名的正是这个竞态:第一次渲染在快照加载之前就触发了 persist effect,把 seed 写在了已保存数据之上。当提示词把"找 bug"变成交付物时,便宜的模型也能找到真 bug。 检查:两个状态,都可到达。让 sim 烧 20 秒,刷新,数字继续。清掉 key,刷新,干净的 seed 回来了。 # 在浏览器控制台里: localStorage.removeItem("meridian.snapshot") # 你的会不同;然后 reload BUILD 5:操盘手界面 为什么,一行:操盘手按固定顺序问三个问题(正在发生什么、需要我吗、我该做什么),而主屏幕必须从上到下回答它们。 第一性原理。从问题推导驾驶舱,而不是从截图里好看的东西推导: 正在发生什么:一个北极星数字加一条实时 feed。 需要我吗:一个风险雷达(谁被卡住、谁超预算)和一个待决决策的计数。 我该做什么:其中每一个都是一次点击的深度,而不是一次搜寻。 而且因为公司是状态,每个 widget 都是对 store 的纯读取。一个缓存自己数字的驾驶舱,是一个说谎的仪表。 通用化提示词: i need the one screen i actually stare at all day. im the operator. when i open it it answers, in this order: whats happening, does it need me, what do i do about it. so: the one number that says are we winning, whos on fire or over budget, whats waiting on my yes, how fast money is burning per team, and a live feed of what just happened. every widget just reads the store, nothing owns its own state, everything that needs me is one click from here. build the shell + this cockpit, top to bottom in that order. 参考实现。一个侧边栏外壳(导航、公司切换器、sim 开关)和 CommandView:带增量的北极星、风险雷达、深度链接到门的待批审批计数、来自 company.budgets 的部门烧钱条、最近 20 条活动 feed。全是 selectors,没有本地 interval,机器值用等宽字体。 Context7 再次挣到了它的口粮:React 19 的 memoization 指导是现成的,所以每次 tick 的重渲染保持便宜,不需要过时 API 的变通。 驾驶舱是一个操盘手界面;Work 看板是另一个,从同一个 store、同样的 token 构建。一旦 BUILD 2 存在,每个界面都是看向它的一扇纯窗。 检查:冷启动打开驾驶舱,不点击,十秒内大声答出那三个问题。切换公司;每个 widget 都换,没有陈旧的数字渗漏。点审批计数;你落在门上。 BUILD 6:门 为什么,一行:权力流经门,所以任何花钱、雇佣、交付或解雇的事,没有一次明确记录在案的"是",都不解决。 第一性原理。自主权是预算,不是权利。那五个可能伤害你的动词(花钱、雇佣、覆盖、发布、终止),在决策时刻各自需要同样四样东西: 诉求(the ask) 诉求者的理由 机器自己的策略检查,公开论证 一个带着名字和时间戳、落进永久日志的决策 还有一样,容易漏:当一次策略检查失败时,批准必须感觉像在覆盖。默认值正是治理死亡的地方。 通用化提示词: heres the rule for the whole thing: an agent never spends money, hires, ships something public, or fires anyone without me saying yes. build me the one inbox where that yes lives. every item shows the ask, whos asking, why, how much, and the systems own policy checks (in budget? is there a manager? under the cap?) so i see its reasoning before i decide. approve and reject both leave a permanent trace in the log with my name on it, not just a ui toggle. and if a check failed, dont make approve the easy default, make me override on purpose. build the inbox. 门。每张卡片显示诉求、请求者、理由、金额、以及系统自己的策略检查。批准和拒绝是唯一的出口,两者都写进日志。 参考实现。ApprovalsView:待批在前,每张卡片带类型徽章、请求者、理由、等宽金额、以及带详情文本的通过/失败策略检查。默认值翻转那一行,正如提示词要求的那样: const failed = a.checks.some((c) => !c.passed); // ... <button className={failed ? "danger" : "primary"} onClick={() => dispatch({ type: "decideApproval", id: a.id, approve: true })}> {failed ? "Override and approve" : "Approve"} </button> 决策本身是 BUILD 2 的 decideApproval case,这正是要点:门是一个屏幕,但法则活在 reducer 里。一道只在 UI 里强制的门,是一个建议。 检查:批准一个种子项;它移到已决策,盖章"由你批准",一条 governance 行落进 feed。找一个检查失败的项;按钮读作"Override and approve"。 然后看 sim 一分钟:如果有任何审批在你不点击的情况下自己解决了,门就是坏的,而它修好之前,其他什么都不重要。 BUILD 7:命令行 为什么,一行:操盘手发布命令,而一条要花一次模型调用去解析的命令,比一个正则更慢、更贵、更不确定。 第一性原理。操盘手的话有两种。命令("create task x, assign to bea, p1"、"move MER-1042 to review"、"budget report")有固定语法和一个已知动作:本地解析它们,派发真实的 store action,打印确切改变了什么。总成本零,延迟零。 其他一切都是对话,而那才是模型该干的事。路由规则是确定优先、模型作兜底,并且永远显示追踪。一个默默做事的 OS,和一个什么都不做的 OS 无法区分。 通用化提示词: fastest way to run this company is a command line, not clicking around. i want a chat where i type "create task: fix onboarding, assign to bea, p1" or "move MER-1042 to review" or "budget report" and it just DOES it, hits the store, shows me exactly what changed. no model call, no cost, no waiting, when its a known command. only when nothing matches does it fall through to an actual model later. parse first, dispatch the real action, print the trace. and dont route to a model for anything you can just execute. 输入的命令直接命中 store 并打印追踪,没有模型调用。只有一句非命令的话才会落到本地 K3 运行时,这里显示它以 kimi -p(本地,k3)的追踪作答。 参考实现。 KimiSpaceView:对命令语法做正则解析,通过 store 的 selectors 做名字到 id 的解析,派发,把追踪行推回聊天。兜底分支在这个构建里打印一个占位符,因为模型是 BUILD 8 的问题: if ((m = input.match(/^create task:\s*(.+?),\s*assign to\s+(\w+),\s*(p[0-3])$/i))) { dispatch({ type: "createTask", title: m[1], assigneeId: agentIdByName(m[2]), priority: m[3] as never, by: "you" }); next.push({ role: "system", body: `Created task "${m[1]}" (${m[3]}) -> ${m[2]}` }); } else { next.push({ role: "assistant", body: "(would route to local kimi runtime)" }); } 检查:create task: refresh onboarding emails, assign to Bea, p1 创建一个带生成编号的真实任务,并打印追踪。budget report 即时打印每个部门相对上限的花费,没有 spinner,因为没有模型被调用。一句没意义的话命中占位符。 一个显示加载状态的命令处理,是一次你正在付钱、而且本不该付的模型调用。 BUILD 8:真实运行时 为什么,三行:到目前为止的一切都跑在模拟上,而一个从不接触真实 Agent 的公司 OS 是一个立体模型。最后一个元素是到真实运行时的桥,而它是系统里最危险的文件,因为它派生一个会思考、会花钱的进程。 所以栅栏就是特性:并发为一、一个输入上限、一个击杀计时器、一个隔离的工作目录、以及永远不回传的凭据。 第一性原理。无论你的运行时是什么(一个 CLI、一个 API、一个队列),桥都需要同样的五面墙,每一面回应一种攻击: 一次多少个:一个,否则一次卡住的运行会变成踩踏。 输入多大:封顶,否则有人把一本书贴进你的预算。 多久:一个击杀计时器,否则一次挂起永远占着锁。 哪里:一个专用工作目录,否则 Agent 会读你的仓库。 什么泄漏回来:什么都不。任何响应里都永远没有 token 或凭据。 外加一条优雅规则:如果运行时缺席,降级到模拟,永不崩溃。控制台必须比它的 Agent 活得久。 通用化提示词: ok everything so far is fake, a nice sim. now wire in my real agent. i have a real cli installed and logged in on this machine. when i type something that is NOT a known command, spawn the real agent and answer with the actual model, my own creds. but this is the scariest path in the app, its spawning a process that can spend, so fence it hard and tell me the fence before you build it: how many run at once, how big an input, how long before you kill it, where it runs, what must never leak back out. and if the cli isnt there, dont crash, stay in sim mode. 参考实现。Kimi 在这里既是构建者,也是被构建者:桥派生的运行时,正是生成上面每一个文件的同一个 ~/.kimi-code/bin/kimi,以 kimi -p 调用,把操盘手的消息放在 stdin 上,复用 kimi login 的凭据。 栅栏,如交付所示: 一次一个聊天(第二个并发请求拿 HTTP 409) 8,000 字符输入上限 180 秒击杀计时器 一个专用 .kimi-runtime 工作目录 没有任何返回 token 的端点 两个端点:GET /local-runtime/status 和 POST /local-runtime/kimi/chat。而且因为桥派生一个本地进程,dev server 只绑定到 127.0.0.1: export default defineConfig({ plugins: [react(), kimiOAuthProxy(), localKimiBridge()], // 本地优先:桥派生你的 cli,永远不要暴露到这台机器之外 server: { port: 4173, host: "127.0.0.1" }, preview: { port: 4173, host: "127.0.0.1" }, }); 最后一次 kimi -p 编辑,把 BUILD 7 的占位分支换成了真实 POST,离线兜底保持完好。 检查:证明栅栏,而非特性。Status 端点报告 CLI;一句非命令的话被真实模型回答。然后攻击它:同时两个聊天,第二个返回 409;贴 9,000 字符,派生前被拒;重命名 CLI 二进制,应用仍在 sim 模式运行。 curl -s http://127.0.0.1:4173/local-runtime/status 上线铺开 一个你周末建出来的 OS,仍然要用几周被采纳。毕业吧。 第 1 周,观察。Build 0 到 5,仅模拟。看 tick 移动钱和工作。当你能在十秒内答出驾驶舱的三个问题时毕业。 第 2 周,门。Build 6 和 7。决策种子审批;用输入的命令运营公司。当你做的每个决策都在日志里显示一条匹配的 governance 行时毕业。 第 3 周,连接。Build 8。真实运行时回答聊天,自动巡航保持关闭。当你攻击它们时,409、8k 上限和击杀计时器都触发时毕业。 第 4 周,运营。一个真实 Agent、一个真实任务、一小笔预算。审阅每一次运行。当一整天过去、没有一分你没预料到的花费、没有一个你没看到的决策时毕业。 第 4 周是数字停止被模拟的地方。部门烧钱、预测、以及一个模型/token 账本,每一个真实的美元都可追溯到一个 Agent 和一个任务。 每一行解锁下一行。第 1 天就上第 4 周,是你给一张教育发票筹资的方式。 运行手册 系统发出的每一个警报,以及对应动作。信号对任何公司 OS 都是通用的;动作是参考指向你的地方。 Feed 冻结,sim 开着。interval 重复或缺失,或者某个视图变异了状态。一个 setInterval,只在 provider 里。视图是窗。 数字在刷新时重置。快照在 hydrate 之前写了,或者根本没写。检查 persist effect 上的 hydrated 门。 审批自己解决了。某个不是 decide action 的东西翻了状态。只有 decideApproval 可以碰审批状态;审计 reducer。 烧钱条超过 100%。某个部门过了它的上限。一道花费门应该已经待批;如果没有,检查缺失了。 命令没反应。语法没匹配,或某个名字没解析。回显解析;确认名字存在于 store 里。 聊天返回 409。一次真实运行已经在飞。等。一次一个的墙在工作。 聊天返回 502。认证代理够不到上游。网络问题;应用必须仍在 sim 模式运行。 Status 说 CLI 缺失。运行时缺席或没登录。跑 kimi login,或留在模拟里。永不崩溃。 一次生成后类型检查失败。生成的文件从模型漂移了。修提示词,重新生成。手工修补的文件是不可复现的构建。 规则(打印出来) 公司是状态:一棵带类型的树,每个屏幕都是一扇窗,永远不是来源。 七个名词,各一个定义。两个重叠的名词,是你将会交付的一团糟。 永不空启动。模型里每个状态在 seed 里至少出现一次。 状态在一个 reducer 里改变,否则它不改变。一个不派发的按钮是死的。 心跳有界:封顶的花费、封顶的进展、日志封顶 500。 领域状态持久化;会话状态永不。永不在 hydrate 之前写。 权力流经门:花钱、雇佣、覆盖、发布、终止都排队等一个"是"。 一次失败的策略检查让批准成为一次显式的覆盖,永远不是默认。 门的法则活在 reducer 里。一道只在 UI 里强制的门是一个建议。 没写下来的就没有发生。每个决策都记下名字和时间戳。 确定优先,模型作兜底。永远别付钱让模型解析一个正则。 任何真实运行时上的五面墙:一次一个、封顶输入、击杀计时器、隔离工作目录、零凭据泄漏。 运行时死了,控制台活着。CLI 缺席意味着 sim 模式,永不崩溃。 绑定本地,127.0.0.1。一个派生你 CLI 的桥,不是你会暴露的东西。 修提示词,永远别修文件。K3 是 worker;升级一个构建,不是整个项目。 收尾 仓库从来不是重点。Meridian 是在一个技术栈里、对九个不关心你技术栈的要素的一次实现:词汇、世界、真相、心跳、记忆、界面、门、命令行、运行时。 在 Rails 或 Rust 或一个带宏的电子表格里构建那九个,你就有一个公司 OS。跳过门或日志,你就有一个带 UI 的漏洞,换任何语言都是。 而注意真正构建它的是什么:一个 worker 层模型、两个 skills、九个提示词、以及每个之后的一次检查。你刚刚读的方法,就是它产出的机器。你写规格,一个便宜的 Agent 写文件,墙让每个人都诚实,包括你。 今晚:写 BUILD 0。打开你的 Agent,交给它词汇提示词,让它说出你公司的七个名词。别让它写哪怕一个行为。名词就是整个第一晚,而其他一切都是看向它们的一扇窗。 所以这里有一个值得争论的问题:你公司的七个名词是什么,而哪两个你差点合并了?今晚就构建 BUILD 0,回复你的 types。 从我构建参考仓库时的笔记写就;每个文件都由 Kimi K3 通过 Kimi Code CLI 生成,而一个前沿模型编辑了这篇散文、并处理了一个升级的构建(reducer)。这些说法都可以对着(https://github.com/codejunkie99/meridian-company-os)核对。 本文由作者使用 Kimi K3 和 Kimi Code CLI 构建时的笔记写成,并经过 Kimi K3 Code 和 Opus 4.7 编辑。 标签:# X # AI # Design # Claude # Marketing # Growth # Guide # Chatgpt # Fable 相关文章 AI keeps shipping the same generic UI, here's the cure i'm going to show you why every AI generated UI on the internet looks like one website wearing 40 different icons, and the simplest unsexiest tool that fixes it for you. Design AI Claude Marketing

原文参考:https://maxed.wiki/posts/how-to-build-a-company-os-using-kimi-k3-builder-s-guide/ (Maxed.wiki,本页为站内中文整理)