1. 为什么团队开始找 ChatGPT Work 的平替
ChatGPT Work 这类产品把「对话」做得很顺,但团队真正落地时会撞上三堵墙:一是任务跑完只留下聊天记录,没有可审计的文件、分支和提交;二是 Agent 能碰哪些系统、用哪些密钥,全靠产品默认策略,团队没法按最小权限收缩;三是多个任务并行时共享同一套上下文,互相污染,出了问题也难回溯。
Kortix(仓库名仍是 suna)给出的答案是把 AI 工作流当成一套可版本化的工程系统:用户发起一个 Session,平台为它切出独立 Git 分支、拉起隔离沙箱,OpenCode Agent 在真实 Linux 环境里读写文件、跑命令、调外部系统,最后把值得保留的成果通过 Change Request 送回默认分支。Agent、Skills、项目记忆、Trigger 和权限配置全部存在 Git 仓库里,随代码审查和版本回滚。
这套东西适合谁?需要「读资料—用工具—生成文件—提交结果」闭环的团队,比如研究报告、数据分析、代码修改、运维巡检、销售支持自动化。它不适合只想找个聊天窗口的人,也不适合指望开箱即得向量知识库 RAG 的场景。下面我把 Git 驱动的 AI Management System 和 OpenCode Agent 隔离沙箱的落地配置拆开讲,包括可复制的config.toml、settings.json骨架,以及用 TaoToken 统一 Key/API 通道接入的步骤。
2. 前置准备:TaoToken 统一 Key 与 API 通道
Kortix 的模型请求走 LLM Gateway,你可以自带模型 Key,也可以接一个统一网关。团队里多个 Agent、多个 Session 并行时,如果每个都配一套供应商 Key,轮换和审计会很痛苦。我习惯用 TaoToken 做统一入口:一个 Key 覆盖多种模型,调用记录集中,换模型不用改 Agent 配置。
先拿到访问凭证。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册后进入控制台,在 API Keys 页面创建一个项目级 Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建时建议按项目分 Key,比如kortix-dev、kortix-prod,方便后续按项目统计用量和吊销。
API 基地址是 https://taotoken.net/api ,注意这个地址不带 UTM 参数,直接写进配置即可。它兼容 OpenAI 风格的/v1/chat/completions和 Anthropic 风格的/v1/messages,所以 Kortix 的 LLM Gateway 无论按哪种协议转发都能对上。
注意:Key 只放在服务端环境变量或 Kortix 的 Secret 里,不要写进
kortix.yaml提交到 Git。Manifest 里只引用 Secret 名称,真实值由控制平面解析。
如果你只是想先验证模型通道是否通,不用急着搭 Kortix,可以直接在模型对话页试一条请求:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。确认返回正常后,再把同一个 Key 接到 Kortix 的 LLM Gateway。
3. 可复制配置:config.toml 与 settings.json 骨架
Kortix 的配置分两层:Git 仓库里的kortix.yaml(Manifest,管 Agent 授权、Connector、Trigger)和运行时的config.toml/settings.json(管 OpenCode 内核、模型通道、沙箱参数)。前者进版本控制,后者放本地或注入环境。
3.1 config.toml:OpenCode 内核与模型通道
config.toml主要给 OpenCode 和沙箱守护进程读。下面这份骨架把模型通道指向 TaoToken,并把沙箱资源限制写清楚:
# config.toml —— OpenCode 内核与模型通道 [server] hostname = "127.0.0.1" port = 4096 [model] # 统一走 TaoToken 网关,协议按 OpenAI 兼容 provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-sonnet-4-5" fallback_model = "gpt-4.1" [model.limits] max_tokens = 8192 request_timeout_sec = 120 max_retries = 3 [sandbox] provider = "daytona" # 也可换 platinum / e2b image = "ubuntu:24.04" cpu = 4 memory_gb = 8 disk_gb = 50 idle_timeout_sec = 900 # 空闲回收,配合 60s 续租 [sandbox.env] # 只注入本任务必需的运行时 Secret 名称 KORTIX_CLI_TOKEN = "${KORTIX_CLI_TOKEN}" TAOTOKEN_API_KEY = "${TAOTOKEN_API_KEY}" [git] default_branch = "main" session_branch_prefix = "session/" auto_push = true # Agent 完成后推送 Session 分支 require_cr = true # 默认分支必须经 Change Request 合并几个关键点。base_url指向 TaoToken 的 API 地址,api_key_env声明从哪个环境变量读 Key,这样 Key 不进配置文件。require_cr = true是这套系统的核心约束:Agent 在 Session 分支里怎么折腾都行,但默认分支不会被动,必须人工看 Diff 后合并。
3.2 settings.json:Agent 授权与工具边界
settings.json更贴近 Agent 运行时行为,管工具发现、Skill 加载、Connector 暴露方式:
{ "agent": { "name": "researcher", "mode": "primary", "model": "claude-sonnet-4-5", "temperature": 0.3, "tools": { "read": true, "edit": true, "bash": true, "task": true, "web_search": true, "scrape_webpage": true }, "permission": { "bash": "ask", "edit": "allow" } }, "skills": { "load_mode": "lazy", "allowed": ["competitive-analysis", "presentations"] }, "connectors": { "discovery": "cli", "mcp_meta_tools": true, "required": ["github-read"] }, "memory": { "root": ".kortix/memory", "auto_inject": false, "index_file": "MEMORY.md" }, "sandbox": { "browser_enabled": true, "connectors_mcp_enabled": true } }skills.load_mode = "lazy"对应前面说的渐进披露:OpenCode 先发现 Skill 名称和描述,任务匹配后才加载SKILL.md正文,不把几十份教程塞进每轮 Prompt。memory.auto_inject = false是真实设计,Agent 必须主动调memory view读索引,再按任务相关性读子文件。connectors.discovery = "cli"表示外部 Action 不一次性变成 LLM 工具,而是先discover再describe最后call。
3.3 kortix.yaml:Manifest 授权骨架
Git 仓库根目录的kortix.yaml管「这个 Agent 能碰什么」,和settings.json里「模型怎样做事」分开:
kortix_version: 2 default_agent: researcher agents: researcher: connectors: [github-read, web-search] connectors_required: [github-read] secrets: none skills: [competitive-analysis, presentations] kortix_cli: [project.read, project.cr.open] sandbox: default: research templates: - slug: research image: ubuntu:24.04 cpu: 4 memory: 8 disk: 50 policy: default_mode: riskconnectors_required必须是connectors的子集,启动前解析不到必需 Connection 会直接返回409 CONNECTOR_CONNECTION_REQUIRED,避免 Agent 干到一半才发现关键系统不可用。kortix_cli只给project.read和project.cr.open,意味着这个 Agent 能开 CR 但不能自己合并,合并权限留给人工。
4. 验证请求:从 Session 到 Change Request
配置写完,跑一条最小链路验证。先确认模型通道通,再确认沙箱和 Git 流程通。
4.1 验证 TaoToken 通道
在沙箱或本地先发一条请求,确认 Key 和地址对:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "reply with ok"}], "max_tokens": 16 }'返回里能看到choices[0].message.content就说明通道正常。如果返回 401,检查 Key 是否带上了Bearer前缀;返回 404 多半是base_url写成了带/v1的完整路径又重复拼接。
4.2 创建 Session 并观察沙箱
用 CLI 走一遍:
kortix login kortix init my-ai-team cd my-ai-team kortix ship kortix sessions new --prompt "分析本周提交并生成一份带来源的项目周报" kortix sessions chatSession 创建后,控制平面会做一串动作:解析 project、agent_name、base_ref、sandbox template 和 provider;校验账户并发上限、Agent 是否启用、必需 Connector 是否可用;计算user role ∩ agent grant,解析允许注入的 Secret;创建project_sessions/session_sandboxes记录;从默认分支切出以session_id命名的分支;异步请求 Daytona 创建沙箱;注入项目、分支、Agent、Token、模型网关和环境变量;kortix-agent克隆仓库并启动opencode serve。
session_id、分支名和sandbox_id用同一个 UUID,日志和审计能沿同一标识串起来。Session 状态实际写入provisioning、running、stopped、failed,沙箱状态用provisioning、active、stopped、error、archived。枚举里还有queued、branching、completed,但当前流程不写入,客户端别把它们当活跃生命周期。
4.3 检查成果与合并 CR
Agent 干完活,先看分支和 CR:
kortix cr ls kortix cr merge 1完整成功判据是:Session 进入可工作状态,沙箱里产生了预期文件或提交,Session 分支已推送,Change Request 能看到真实 Diff;执行kortix cr merge 1后默认分支出现预期成果。只有聊天界面出现回答、但没有文件、分支或 CR,不能证明交付链跑通。
合并前服务端会读候选分支里的kortix.yaml做 Manifest 校验。无效配置返回422 MANIFEST_INVALID,Git 冲突返回409和冲突文件列表。project.cr.open和project.cr.merge是分离能力,所以可以让 Agent 开 CR 却不给它自我合并的权限。
5. 本篇常见错排查
5.1 沙箱起不来或 Session 卡在 provisioning
先看kortix self-host status和kortix self-host logs。常见原因是 Sandbox Provider Key 没配或配额用尽。自托管时控制平面在本地 Compose,但 Agent 沙箱默认仍在外部 Provider(Daytona / Platinum / E2B),远程沙箱需要主动访问 Kortix API,所以必须有稳定回调地址。没有域名或 Tunnel,Session 跑不起来。Tunnel URL 每次重启会变,只适合试用。
5.2 模型请求 401 / 429
401 检查TAOTOKEN_API_KEY是否注入到沙箱环境,以及config.toml里api_key_env名字是否一致。429 分两种:一种是 TaoToken 侧的速率限制,去控制台看用量;另一种是 Kortix 账户级并发 Session 上限,超过套餐限制返回 429。活跃 Turn 每 60 秒续租一次阻止 Idle Reaper,中止或空闲后释放。
5.3 Connector 调用被拒或一直 pending
409 CONNECTOR_CONNECTION_REQUIRED说明connectors_required里的 Connection 没解析成功,去 Customize 区域检查 Connector 是否已连接。如果返回202 pending_approval,说明策略命中了require_approval,需要用户登录并具备项目权限后批准。注意审批不是「本 Session 后续都允许」:Gateway 对 Connector、Action 和完整参数算请求摘要,收件人、正文、URL 变了旧审批不能复用。
5.4 CR 合并报 422 或 409
422 MANIFEST_INVALID是候选分支的kortix.yaml校验没过,常见于connectors_required不是connectors子集,或字段拼写错。409是 Git 冲突,返回里带冲突文件列表,需要人工在分支上解决后再合并。别让 Agent 自动合并,project.cr.merge权限默认不给 Agent。
5.5 记忆没生效
.kortix/memory不是自动注入的。Agent 必须先调memory view读MEMORY.md索引,再读相关子文件。如果 Agent 没读,检查 Prompt 里有没有要求先看索引,以及settings.json里memory.root路径对不对。记忆修改是普通 Git 文件变更,必须经 Session 分支和 CR 才能成为默认分支上的团队记忆。
6. 把通道和权限收口到一处
这套系统真正值得借鉴的是四个工程原则:配置进 Git、执行进沙箱、权限在服务端、结果经审核合并。落地时最容易忽略的是模型通道和权限的收口——Agent 越多、Session 越并行,散落的 Key 和授权越难审计。
模型通道我建议统一走 TaoToken,一个 Key 覆盖多种模型,调用记录集中,换模型不用改 Agent 配置。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 OpenAI 和 Anthropic 两种协议的对接示例。如果你要长期跑编码类 Agent 或自动化任务,可以看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,按用量规划比临时加 Key 更省心。
权限侧记住三层交集:用户项目角色 ∩ Agent Manifest Grant ∩ Connector Policy。Manifest 对未声明的 connectors、secrets、skills、kortix_cli 默认是 none,Starter 里那个all是模板选择不是安全结论,生产项目要主动收缩。沙箱里KORTIX_SANDBOX_TOKEN和KORTIX_CLI_TOKEN别混用,前者是沙箱身份,后者是启动者项目身份并受 Agent Grant 收缩。Git Token 也不是静态注入,守护进程用 Sandbox Token 换短期 Clone Credential,推送只能进 Session 分支。
最后一句实操建议:第一轮别同时开所有 Connector 和 Secret。先用默认 Agent 加一个无副作用任务打通 Session、文件产出和 CR,再逐步加 Skill、只读 Connector、审批策略,最后才上写操作、定时触发和多 Session 并行。