1. 从 spark.v1.request.build 到 system prompt:capsule-identity 的组装链路
一旦 Agent 总线里出现spark.v1.request.build,capsule-identity 就会进入工作状态。它不是一个独立 CLI,而是 Unicity AOS 里的一个 capsule,Rust 实现,MIT 与 Apache-2.0 双许可,GitHub Star 约 7000。一个容易记住的类比是,它在这套 Agent OS 里扮演的角色接近 Linux 的/etc/profile——启动时确定“我是谁、我在哪、我该遵守什么”。
身份信息不是写死在代码里。它先尝试从 capsule 的 KV 状态读取已持久化的 spark 身份;如果 KV 为空,就去探测home://.config/spark.toml。随后把当前工作目录、运行平台等环境上下文追加进去,最后在spark.v1.response.ready主题上发布拼好的系统提示词。这个顺序很关键:身份优先级高于文件,文件高于默认引导,环境信息最后合并。
如果系统里还没有身份,发布出去的提示词会带一段 onboarding 说明。LLM 走完引导后调用save_identity,把呼号(callsign)、类别、气质、信号、核心指令等写入胶囊状态,同时落一份home://.config/spark.toml作为恢复副本。状态按 principal 隔离,会话 ID 也会回传,方便多轮循环关联。对外接口是 IPC 主题:订阅spark.v1.request.build、tool.v1.execute.save_identity,发布spark.v1.response.ready。编译测试是标准 Rust 流程,cargo build、cargo test,MSRV 1.94。
对提示词工程开发者来说,这意味着 system prompt 不再是一段散落在.env、YAML、数据库和代码字符串里的文本,而是一个有生命周期、可持久化、可恢复、按主体隔离的模块产物。接下来真正要消耗 Token 的动作,是把这段产物交给 LLM。初始化 Key 时直接去 TaoToken 官网:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=capsule_identity_intro ,Base URL 填https://taotoken.net/api。这样 capsule-identity 负责“组装”,TaoToken 负责“调用”。
但要把整条链路跑顺,得先厘清一个边界:capsule-identity 只解决“身份从哪里来、怎么拼装、怎么恢复”,它不替代模型网关。Agent 运行时拿到 system prompt 后,还需要模型名、API Key、Base URL、超时、重试、会话关联这些参数。很多团队把身份管理和模型调用混在一起,最后变成提示词里硬编码 Key,或者把 Key 塞进 YAML 再由 Agent 动态读取。这种做法的风险很直接:轮换困难、审计困难、多主体隔离困难。更合理的拆分是让 capsule-identity 输出结构化身份,再由调用层统一走 TaoToken。
下面我们按“组装结果 → 调用参数 → 工具配置 → 排障”的顺序,把可复现实验串起来。
2. 初始化 TaoToken:从 Key 到最小可调用请求
在 capsule-identity 发布系统提示词之后,Agent 需要调用 LLM。此时第一步不是写业务逻辑,而是把模型访问入口配好。去 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=api_key_setup 创建 API Key,然后在调用层使用:
- API Key:
YOUR_API_KEY - Base URL:
https://taotoken.net/api - 认证方式:
Authorization: Bearer YOUR_API_KEY - 模型名:以 TaoToken 控制台或模型对话页当前可用列表为准
注意 Base URL 不要带 UTM 参数,工具配置里只写https://taotoken.net/api。UTM 只用于官网入口统计,不要混进 SDK 的base_url。
先用curl做最小验证:
export TAOTOKEN_API_KEY="YOUR_API_KEY" curl -sS https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer ${TAOTOKEN_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4.1-mini", "messages": [ { "role": "system", "content": "你是 capsule-identity 组装的 Agent 身份。请用一句话说明你是谁。" }, { "role": "user", "content": "现在开始自我介绍。" } ], "temperature": 0.2, "max_tokens": 128 }'如果返回 401,优先检查 Key 是否完整、是否带了Bearer前缀、环境变量是否被 shell 引号截断。如果返回 404,优先检查 Base URL 是否写成https://taotoken.net/api,而不是多拼了/v1/chat/completions或用了错误协议头。模型名不要凭记忆写,去控制台确认当前账号可用的模型标识。
这一步的意义是建立“模型访问基线”。只有当最小请求稳定可用,后面 capsule-identity 的 system prompt 注入才有调试价值。否则提示词写得再好,也会被 401、404、429 掩盖。
3. 把 capsule-identity 的组装结果映射成 LLM 调用参数
假设 capsule-identity 在spark.v1.response.ready上发布了一份组装结果,我们可以把它抽象成下面这种结构:
{ "session_id": "spark-session-7f3a", "principal": "agent:writer-01", "identity": { "callsign": "Atlas", "category": "技术写作 Agent", "temperament": "直接、稳定、先给结论再给依据", "signals": ["技术博客", "Agent 工程", "API 接入"], "core_instructions": [ "不编造未验证的接口参数", "配置示例必须可复制", "涉及 Key 时使用占位符" ] }, "environment": { "cwd": "/home/agent/workspace", "platform": "linux-x86_64" }, "system_prompt": "你是 Atlas,类别:技术写作 Agent。气质:直接、稳定、先给结论再给依据。信号:技术博客、Agent 工程、API 接入。核心指令:不编造未验证的接口参数;配置示例必须可复制;涉及 Key 时使用占位符。当前工作目录:/home/agent/workspace。运行平台:linux-x86_64。" }这份结构和 LLM 调用参数的映射关系如下:
| capsule-identity 字段 | LLM 调用参数 | 说明 |
|---|---|---|
system_prompt | messages[0].content,且role=system | 系统提示词主体 |
session_id | 请求头X-Session-Id或网关支持的metadata.session_id | 用于多轮关联 |
principal | metadata.principal或调用日志字段 | 用于按主体隔离与审计 |
identity.callsign | 已合并进system_prompt | 不建议再拆成独立参数 |
environment.cwd | 已合并进system_prompt | 若目录敏感,先脱敏 |
| 模型选择 | model | 由调用层决定,不写入身份模块 |
| 温度 | temperature | 身份描述用 0.1~0.3 更稳 |
| 最大输出 | max_tokens | 先设小值验证,再逐步放大 |
下面用 Python 模拟 capsule-identity 的组装函数,并调用 TaoToken:
import os import json import requests def build_system_prompt(identity: dict, env: dict) -> str: callsign = identity.get("callsign", "未命名 Agent") category = identity.get("category", "通用助手") temperament = identity.get("temperament", "稳定、直接") signals = identity.get("signals", []) core_instructions = identity.get("core_instructions", []) lines = [ f"你是 {callsign},类别:{category}。", f"气质:{temperament}。", f"信号:{', '.join(signals) if signals else '无'}。", "核心指令:", ] lines.extend(f"- {item}" for item in core_instructions) lines.append(f"当前工作目录:{env.get('cwd', '未知')}") lines.append(f"运行平台:{env.get('platform', '未知')}") return "\n".join(lines) identity = { "callsign": "Atlas", "category": "技术写作 Agent", "temperament": "直接、稳定、先给结论再给依据", "signals": ["技术博客", "Agent 工程", "API 接入"], "core_instructions": [ "不编造未验证的接口参数", "配置示例必须可复制", "涉及 Key 时使用占位符", ], } env = { "cwd": "/home/agent/workspace", "platform": "linux-x86_64", } system_prompt = build_system_prompt(identity, env) print("=== system prompt ===") print(system_prompt) api_key = os.environ["TAOTOKEN_API_KEY"] base_url = "https://taotoken.net/api" payload = { "model": "gpt-4.1-mini", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": "请用三句话介绍你的身份、当前工作目录和一条核心指令。"}, ], "temperature": 0.2, "max_tokens": 256, "metadata": { "session_id": "spark-session-7f3a", "principal": "agent:writer-01", }, } resp = requests.post( f"{base_url}/chat/completions", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, data=json.dumps(payload), timeout=60, ) resp.raise_for_status() result = resp.json() print("=== assistant ===") print(result["choices"][0]["message"]["content"])如果你的网关不支持metadata,就把session_id和principal放到自定义请求头里,例如X-Session-Id和X-Principal,同时在本地日志中记录。这样做的目的是让 capsule-identity 的身份隔离和 LLM 调用日志能对齐。
这里有一个工程细节:不要直接把 capsule KV 里的全部状态都塞进 system prompt。身份模块的 KV 可能包含恢复副本、内部标记、权限上下文,而 LLM 只需要“可以公开表达的身份描述”。在组装阶段做一层白名单,只把callsign、category、temperament、signals、core_instructions和环境中的非敏感字段写入 prompt。否则一旦 KV 被污染,模型输出也可能被带偏。
可复现产出可以整理成一张对照表:
| 实验步骤 | 输入 | 输出 | 检查点 |
|---|---|---|---|
| 触发身份组装 | spark.v1.request.build | 结构化身份 JSON | KV 是否命中,是否探测spark.toml |
| 生成 system prompt | 身份 JSON + 环境上下文 | 一段完整系统提示词 | 是否包含呼号、类别、核心指令 |
| 调用 TaoToken | system prompt + 调用参数 | LLM 回复 | 401/404/429 是否出现 |
| 多轮关联 | session_id+principal | 日志可追踪 | 是否按主体隔离 |
4. 同一条身份 prompt 在 Claude Code、Codex、CC Switch 里的落地
capsule-identity 输出的是模型无关的 system prompt,但不同工具读取配置的方式不同。最容易踩的坑是把 Claude Code 的ANTHROPIC_*变量复制到 Codex,结果 Codex 完全读不到。下面拆开写。
Claude Code:settings.json 与 ANTHROPIC_*
Claude Code 侧建议用~/.claude/settings.json或项目级.claude/settings.json管理环境变量。配置示例:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "claude-sonnet-4-5", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5" } }如果你当前使用的 Claude Code 版本读取的是ANTHROPIC_API_KEY,就按版本要求补充同名变量;核心是ANTHROPIC_BASE_URL指向https://taotoken.net/api,认证值使用YOUR_API_KEY。不要把 Base URL 后面追加 UTM 参数,否则会影响 SDK 路由。
Codex:config.toml 与 TAOTOKEN_API_KEY
Codex 使用~/.codex/config.toml,不要混用ANTHROPIC_*。示例:
model = "gpt-5" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"然后在 shell 中导出:
export TAOTOKEN_API_KEY="YOUR_API_KEY"这里的env_key是 Codex 去环境变量里找 Key 的字段名。用TAOTOKEN_API_KEY或OPENAI_API_KEY都可以,但不要写成ANTHROPIC_AUTH_TOKEN,因为 Codex 的 provider 配置不会读 Claude Code 那套变量。
CC Switch:把 TaoToken 当成一条 provider
如果你用 CC Switch 做多供应商切换,核心是维护三件套:provider 名称、Base URL、API Key。配置形态可以抽象为:
{ "name": "taotoken", "baseUrl": "https://taotoken.net/api", "apiKey": "YOUR_API_KEY" }不同版本的 CC Switch 字段名可能略有差异,但本质不变:给 Claude Code 或 Codex 切换时,确保它读取的是同一条 TaoToken provider。你可以把 capsule-identity 生成的 system prompt 放在项目级指令文件或 Agent 配置里,而把模型访问参数统一交给 CC Switch 管理。这样身份层和接入层不会互相污染。
如果你还没有创建 Key,可以回到 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=provider_switch 完成初始化,再把同一把 Key 配置到 Claude Code、Codex 或 CC Switch 中。
5. 排障:提示词组装成功但 LLM 调用失败时怎么查
capsule-identity 的日志里显示spark.v1.response.ready已经发布,system prompt 也拼好了,但 LLM 调用仍然失败。这时不要先改提示词,按下面顺序排查。
第一,看认证。401 通常意味着 Key 无效、过期、复制不完整,或者请求头没有使用Bearer。如果你在 Claude Code 里配了ANTHROPIC_AUTH_TOKEN,在 Codex 里却用TAOTOKEN_API_KEY,检查工具实际读取的是哪一个环境变量。用env | grep -E "ANTHROPIC|TAOTOKEN|OPENAI"可以快速确认当前 shell 里有哪些值。
第二,看路由。404 或连接错误通常来自 Base URL。工具配置里只写https://taotoken.net/api,不要写成https://taotoken.net/api/v1或https://taotoken.net/api/chat/completions再交给 SDK 二次拼接。SDK 和裸 HTTP 请求的路径拼法不同,先用本文的curl验证,再换 SDK。
第三,看模型名。400 里如果出现 model not found,说明请求体里的model不在当前账号可用列表。去 TaoToken 模型对话页确认模型标识,不要直接把其他平台的模型名照搬过来。不同供应商对同一模型的命名可能不同。
第四,看限流和超时。429 表示请求频率或并发超过限制,先降并发,加指数退避。超时则优先检查max_tokens是否过大、system prompt 是否过长、网络是否稳定。capsule-identity 组装出的 prompt 如果包含大量环境信息,建议限制长度,只保留调用需要的最小上下文。
第五,看身份加载。如果 LLM 回复明显不像配置的身份,检查 capsule KV 是否为空、home://.config/spark.toml是否存在、onboarding 是否已经完成、save_identity是否成功写回。可以在本地打印system_prompt,确认呼号、类别、气质、信号和核心指令是否都在。注意不要把含敏感路径的完整 prompt 发到公开日志里。
第六,看会话关联。多轮循环里如果回复串线,检查session_id是否回传并在调用层透传。principal是否按调用主体隔离,决定了不同 Agent 的身份状态会不会互相覆盖。对多 Agent 平台来说,这一步比提示词措辞更重要。
排障时可以在本地先跑一个最小脚本:只发一条 system 消息和一条 user 消息,模型用控制台确认可用的标识,temperature设 0.1,max_tokens设 128。最小请求通过后,再逐步加回 capsule-identity 的完整 system prompt 和 metadata。这样能快速定位问题在身份层、接入层还是模型层。
6. 从模型对话到 Coding Plan:把实验变成可维护的 Agent 调用链
把 capsule-identity 和 TaoToken 接起来之后,建议不要把实验停留在一次性脚本。你可以按下面的路径把链路产品化:
先在模型对话页验证模型可用性和输出风格,确认 system prompt 注入后模型行为符合预期:
https://taotoken.net/models/detail/chat?utm_source=taotoken_aicg_blog_end&utm_content=cta_chat如果你的 Agent 开发流需要长期、稳定的编码调用,查看 Coding Plan 是否匹配当前用量和并发需求:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=cta_coding_plan为正式环境创建独立 API Key,不要复用测试 Key,也不要把 Key 写进代码仓库:
https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=cta_api_keys如果你要把同一套 Base URL 和 Key 接进命令行 Agent,按 Claude Code 文档配置
settings.json或ANTHROPIC_*:
https://taotoken.net/doc/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=cta_claude_code_doc
回到 capsule-identity 的设计思路,它真正值得借鉴的不是某个主题名,而是把“Agent 身份”从散落配置中抽出来,变成可持久化、可恢复、按 principal 隔离的模块。TaoToken 在这条链路里承担的是模型访问层:Base URL 统一为https://taotoken.net/api,Key 用YOUR_API_KEY占位,身份 prompt 由 capsule-identity 组装,调用参数由 Agent 运行时管理。这样拆分之后,换模型、换工具、换环境时,你只需要改接入层,而不是重写身份逻辑。