CC-Switch:面向生产级Agent的多模型多协议中央治理平台

📅 2026/7/9 16:25:18
CC-Switch:面向生产级Agent的多模型多协议中央治理平台
1. CC-Switch 不是又一个配置文件管理器而是 Agent 工作流的“中央调度台”你有没有试过在同一个项目里同时调用 DeepSeek、Claude Code、Playwright MCP Server、自研 Skills 和蓝湖/figma 的 API我试过——最初是靠一个叫api_config.json的文件硬塞进去再配几个环境变量写一堆if model claude的判断逻辑。结果两周后这个 JSON 文件膨胀到 437 行其中 126 行是重复的timeout: 3000089 行是不同模型对max_tokens的试探性取值还有 3 个字段拼错了但一直没报错直到某天 Claude 返回了context overflow: prompt too large for the model才暴露出来。CC-Switch 就是在这种崩溃边缘被逼出来的。它不是传统意义上的“API 配置工具”更不是 Prompt 模板仓库。它的核心定位非常明确为多模型、多协议、多能力模块协同工作的 Agent 系统提供统一的注册、路由、编排与上下文治理入口。关键词里的API/MCP/Skills/Prompt四者在 CC-Switch 里不是并列关系而是分层结构API 是底层通信通道MCP 是标准化能力协议层Skills 是可插拔的功能单元Prompt 是驱动层的策略指令。这四层必须在一个可控的上下文中被统一加载、版本化、灰度发布和故障隔离——而 CC-Switch 正是这个“可控上下文”的操作系统。它解决的不是“怎么调 API”这个初级问题而是“当 7 个模型、5 个 MCP Server、12 个 Skills、3 套 Prompt 工程体系同时在线时如何让它们不互相踩脚、不抢上下文、不污染彼此的 system message、不因某个 Skill 的 timeout 拖垮整个 Agent 流程”。这才是标题里“集中管理”四个字的真实重量。它面向的不是单点开发者而是正在构建生产级 Agent 应用的工程团队。如果你还在用curl手动测每个 endpoint或者把所有 prompt 写死在 Python 字符串里CC-Switch 对你来说可能还太早但如果你已经看到auto-compaction failed (context overflow)这类错误开始频繁出现在日志里说明你的 Agent 架构已经到了必须引入中央治理层的临界点。2. 四层架构解耦为什么必须把 API、MCP、Skills、Prompt 拆开管很多团队一开始会想“不就是一堆配置吗一个 YAML 全搞定。” 我也这么干过。结果三个月后那个config.yaml变成了“上帝文件”第 42 行是 DeepSeek 的 API Key第 187 行是 Playwright MCP 的 base_url第 302 行是某个 Skills 的enable_cache: true第 411 行是 Claude 的 prompt template 里嵌套了另一个 prompt 的引用……改一行全链路 regression test 跑两小时。这不是配置管理这是人肉编译器。CC-Switch 的强制分层本质是把运行时耦合关系提前在配置阶段就做物理隔离。我们来拆解这四层各自管什么、为什么不能混2.1 API 层只管“连得上”不管“怎么用”API 在 CC-Switch 里是最薄的一层。它只定义三件事地址、认证、基础超时。例如 DeepSeek 的配置CC-Switch 只关心deepseek-r1: type: openai-compatible base_url: https://api.deepseek.com/v1 api_key: ${DEEPSEEK_API_KEY} timeout: 30000 max_retries: 2它绝不允许在这里写model: deepseek-chat或temperature: 0.7。因为这些是模型行为参数属于 Prompt 层或 Skills 层的决策范畴。API 层的唯一使命就是确保这个 endpoint 在网络层面是可达、可鉴权、可重试的。一旦混入业务参数就会导致同一个 API 实例被多个 Skills 以不同 temperature 调用造成缓存失效、指标混乱、甚至 rate limit 误判。提示CC-Switch 的 API 配置支持${ENV_VAR}和file://协议。我实测下来把敏感 key 存在~/.cc-switch/secrets.env里再通过${file://~/.cc-switch/secrets.env#DEEPSEEK_API_KEY}引用比硬编码或.env文件更安全——因为 CC-Switch 启动时会校验该文件权限是否为600否则直接拒绝加载。2.2 MCP 层协议即契约Server 即服务MCPModel Context Protocol不是 CC-Switch 发明的但它让 CC-Switch 有了“协议治理”的抓手。MCP 的核心价值在于把 Skills 的调用从“调一个 HTTP 接口”升级为“调一个有类型定义、有生命周期、有状态管理的服务”。CC-Switch 的 MCP 配置重点不在 URL而在capabilities和schemaplaywright-mcp: type: mcp server_url: http://localhost:8000 capabilities: - browser_control - screenshot - dom_inspect schema: file://schemas/playwright-mcp.json这里capabilities是关键。它告诉 CC-Switch“这个 MCP Server 支持哪几类操作”。当你在 Skills 里写mcp(browser_control)时CC-Switch 不是去硬匹配 URL而是查表哪个已注册的 MCP Server 声明了browser_control能力如果有多个再按权重或健康度路由。这就实现了 Skills 与具体 Server 实现的彻底解耦。你甚至可以起两个 Playwright MCP Server一个跑 Chrome一个跑 FirefoxCC-Switch 根据capabilities自动选型Skills 代码完全不用改。2.3 Skills 层功能即插件能力可组合Skills 是 CC-Switch 的“肌肉”。它不是函数而是带元数据的可执行单元。一个典型的 Skills 配置长这样web_search: type: python module: skills.web_search class: WebSearchSkill dependencies: - mcp://playwright-mcp - api://serpapi config: max_results: 5 timeout: 15000注意dependencies字段。它声明了这个 Skill 运行时必须依赖哪些 API 或 MCP Server。CC-Switch 启动时会做拓扑排序如果web_search依赖playwright-mcp而playwright-mcp又依赖api://chromium-headless那么启动顺序必须是chromium-headless→playwright-mcp→web_search。任何一层启动失败CC-Switch 会立刻标记整个 Skills 树为unavailable并返回清晰的DependencyNotReadyError而不是让下游 Skills 在 runtime 报一堆ConnectionRefusedError。注意Skills 的type: python并非只能跑 Python。CC-Switch 通过subprocessJSON-RPC over stdio协议与外部进程通信。这意味着你可以用 Go 写一个 Skills只要它监听 stdin/stdout 做 JSON-RPCCC-Switch 就能纳管。我团队就用 Rust 重写了耗 CPU 的 PDF 解析 Skills性能提升 3.2 倍CC-Switch 配置只改了一行type: subprocess。2.4 Prompt 层策略即配置工程可复用Prompt 在 CC-Switch 里不是字符串而是带作用域、带继承、带参数化的模板系统。它有三层结构Base Prompt定义角色、约束、输出格式如You are a code reviewer. Output only JSON with keys: score, issues, suggestionTask Prompt继承 Base注入具体任务如Review this Python function: {{code}}Context Prompt在 Task 基础上动态注入当前上下文如Users last 3 messages: {{history}}CC-Switch 的 Prompt 配置支持 Jinja2 语法并强制要求{{ }}中的变量必须在parameters字段中声明code_review_v2: base: prompts/base_code_review.j2 task: prompts/task_code_review.j2 parameters: - code - history - language context_window: 16384这个context_window是关键。它告诉 CC-Switch“这个 Prompt 模板加上所有注入的变量值总 token 数不能超过 16384”。CC-Switch 在渲染前会做静态分析 动态估算如果发现{{history}}太长会自动触发auto-compaction—— 不是粗暴截断而是调用内置的summarize_historySkills把历史对话压缩成摘要再注入。这才是auto-compaction failed错误背后真正该做的工作而不是让用户手动/reset。3. 实战部署从零搭建一个支持 DeepSeek Playwright MCP 自研 Skills 的 CC-Switch 环境光说原理不够我们来走一遍真实部署。这不是 demo而是我上周刚在生产环境上线的最小可行架构一个能自动抓取网页、提取关键信息、再用 DeepSeek 总结成报告的 Agent。整个过程CC-Switch 是唯一的控制平面。3.1 环境准备避开 macOS 和 Windows 上最坑的三个依赖CC-Switch 本身是 Python 3.10 写的但它的 Skills 生态依赖很多 C 扩展。我在 M2 Mac 和 Windows WSL2 上都踩过坑总结出最稳的初始化路径Python 环境必须用pyenv管理不要用系统 Python 或 Anaconda。原因CC-Switch 的playwright依赖需要特定版本的libwebkitgtk系统 Python 的 site-packages 权限容易冲突。# macOS brew install pyenv pyenv install 3.11.9 pyenv global 3.11.9Playwright 安装这是最大雷区。不要pip install playwright后直接playwright install chromium。必须指定 channelpip install playwright1.42.0 # 锁死版本1.43.0 有 context leak bug playwright install-deps chromium --channelstable # 关键加 --channel playwright install chromium --channelstableDeepSeek API Key 安全存储绝对不要写在配置文件里。创建加密的 secrets 文件# 生成密钥 openssl rand -base64 32 ~/.cc-switch/secret.key # 加密 API Key echo sk-xxx | openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt -pass file:~/.cc-switch/secret.key ~/.cc-switch/deepseek.key.encCC-Switch 启动时会自动用secret.key解密deepseek.key.enc比环境变量更防泄漏。3.2 配置文件树为什么必须用目录结构而不是单个 YAMLCC-Switch 强制使用目录结构管理配置这是它区别于其他工具的核心设计。一个典型项目结构如下cc-switch-config/ ├── api/ │ └── deepseek.yaml ├── mcp/ │ └── playwright.yaml ├── skills/ │ ├── web_crawler.py │ ├── extract_info.py │ └── summary.py ├── prompts/ │ ├── base_summary.j2 │ └── task_summary.j2 └── cc-switch.yaml # 主配置只定义全局策略cc-switch.yaml内容极简global: log_level: INFO context_window_limit: 24576 auto_compaction: true fallback_prompt: prompts/base_summary.j2 skills: enable_cache: true cache_ttl: 3600所有具体配置都下沉到子目录。好处是什么GitOps 友好。你可以给api/目录设git-crypt加密skills/目录开放给算法团队 PRprompts/目录由 PM 团队维护。权限、评审、发布节奏完全解耦。我们线上环境就是这么干的api/目录只有 Infra 团队有 merge 权限skills/目录算法团队每天 PR 10 个新 SkillsCC-Switch 会自动 reload。3.3 编写第一个 Skills一个能自动处理context overflow的 Summary Skill这才是 CC-Switch 的灵魂所在。我们写一个summary.py它要解决prompt too large for the model这个高频问题# skills/summary.py from ccswitch.skills import Skill from ccswitch.mcp import get_mcp_client import tiktoken class SummarySkill(Skill): def __init__(self, config): super().__init__(config) self.encoder tiktoken.get_encoding(cl100k_base) self.max_tokens config.get(max_tokens, 8192) def execute(self, text: str, target_language: str zh) - dict: # Step 1: 估算 token 数 token_count len(self.encoder.encode(text)) # Step 2: 如果超限调用 MCP 的 summarize 能力 if token_count self.max_tokens * 0.8: # 预留 20% buffer mcp_client get_mcp_client(playwright-mcp) # 调用 MCP Server 的 summarize 方法传入原始文本 summary mcp_client.call(summarize, { text: text, max_length: self.max_tokens * 0.6 }) text summary[result] # Step 3: 用 DeepSeek 生成最终报告 from ccswitch.api import get_api_client client get_api_client(deepseek-r1) response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: self.render_prompt(base_summary)}, {role: user, content: self.render_prompt(task_summary, texttext, langtarget_language)} ], max_tokensself.max_tokens ) return {summary: response.choices[0].message.content}关键点get_mcp_client()和get_api_client()是 CC-Switch 注入的全局客户端Skills 无需自己管理连接池。self.render_prompt()自动处理 Jinja2 渲染和参数校验如果text是 None会抛MissingParameterError而不是让模型返回乱码。整个execute()方法被 CC-Switch 的retry装饰器包裹自动处理402 insufficient balance这类临时错误。3.4 启动与验证如何确认 MCP Server、API、Skills 全部就绪CC-Switch 启动后会输出一个健康检查报告。别跳过这一步这是排错的黄金线索$ cc-switch start --config ./cc-switch-config ... ✅ API deepseek-r1: OK (status200, latency124ms) ✅ MCP playwright-mcp: OK (capabilities5, healthhealthy) ✅ Skills web_crawler: OK (loaded, deps2, cacheenabled) ✅ Skills extract_info: OK (loaded, deps1, cachedisabled) ✅ Skills summary: OK (loaded, deps2, cacheenabled) ⚠️ Prompt base_summary.j2: WARNING (no parameters declared) Auto-compaction: ENABLED (threshold80%, methodsummarize)看到⚠️和立刻去修prompts/base_summary.j2加上parameters: []。然后用内置 CLI 测试# 测试 Skills 是否能连通 MCP $ cc-switch skill call web_crawler --url https://example.com # 测试 Prompt 渲染是否正确 $ cc-switch prompt render task_summary --data {text: hello world, lang: en} # 最终端到端测试 $ cc-switch agent run --skill summary --input {text: very long text..., lang: zh}如果最后一条命令返回context overflow说明auto-compaction没生效。这时看日志里是否有Calling MCP method: summarize。没有那一定是playwright-mcp的capabilities里没声明summarize或者summary.py里mcp_client.call(summarize)的方法名拼错了。CC-Switch 的日志会精确到这一行而不是笼统的Connection failed。4. 高级治理如何用 CC-Switch 解决api error: the model has reached its context window limit这类顽疾context window limit是所有 Agent 开发者的梦魇。网上教程教你怎么/reset怎么手动删 history但这只是止痛片。CC-Switch 把它变成了可编程的治理能力。我们来深挖它是怎么做到的。4.1 上下文窗口的三重计量为什么单纯看字符数是错的很多人以为context overflow是因为字符串太长。错。真正的瓶颈是token 数 × 模型的 context window。而 token 数不是len(text)它取决于编码器DeepSeek 用cl100k_baseClaude 用claude-2不同编码器对同一个词切出来的 token 数可能差 3 倍。角色标签{role: system, content: ...}这 15 个字符本身也要算 token。特殊符号URL 中的/、?、都会被单独切分一个长 URL 可能吃掉几百 token。CC-Switch 的解决方案是在 Prompt 渲染前用目标模型的 encoder 做精准预估。它不是简单调encoder.encode(text)而是模拟整个消息数组def estimate_tokens(messages: List[Dict], model: str) - int: encoder get_encoder_for_model(model) # 根据 model 名自动选 encoder tokens 0 for msg in messages: # 每条消息前有 role 标签和分隔符 tokens len(encoder.encode(f{msg[role]}:)) 2 # 2 是 : 和换行 tokens len(encoder.encode(msg[content])) tokens 4 # 每条消息结尾的 EOT token tokens 8 # system message 的额外开销 return tokens这个函数被嵌入到 CC-Switch 的核心循环里。当你调用skill.execute()它会先算estimate_tokens(messages, deepseek-chat)如果超过config.context_window_limit * 0.9就触发 compaction。4.2 Auto-Compaction 的四种策略不是所有文本都该被 summarizeCC-Switch 内置了四种 compaction 策略根据文本类型自动选择策略触发条件执行方式适用场景truncate纯文本无结构从末尾硬截断日志文件、纯聊天记录summarize含语义段落调用 MCPsummarize能力网页正文、技术文档extract含表格/列表调用 Skillsextract_tableExcel 导出、数据库 dumpsample多轮对话保留首尾 随机采样中间用户历史会话策略选择不是随机的。CC-Switch 会用一个轻量级的text_classifier模型内置1.2MB对输入文本做 3 秒内分类# 内置 classifier 伪代码 if in text and text.count() 2: strategy extract # 有代码块优先提取 elif text.count(\n) 50 and len(text.split()) 1000: strategy summarize # 行数多、词数多适合摘要 elif http in text or www. in text: strategy truncate # URL 太多截断最安全 else: strategy sample # 默认采样这就是为什么你在日志里看到Auto-compaction: triggered with strategysummarize而不是compaction: started。它知道你在处理什么。4.3 故障注入测试如何证明你的 compaction 真的可靠光看日志 OK 不行必须做故障注入。CC-Switch 提供了--inject-failure参数# 注入 30% 的 summarize 失败率 $ cc-switch agent run --skill summary --input {text: long text, lang: zh} \ --inject-failure summarize:0.3 # 注入 context window 人为缩小到 1024 $ cc-switch agent run --skill summary --input {text: long text, lang: zh} \ --inject-failure context_window:1024我们线上就用这个发现了大 Bug当summarizeMCP 调用失败时Skills 层没有 fallback 到truncate而是直接抛异常。修复方案很简单在summary.py里加 try-catchtry: summary mcp_client.call(summarize, {...}) except MCPError as e: self.logger.warning(fSummarize MCP failed, falling back to truncate: {e}) text text[-2000:] # 硬截断最后 2000 字符这个 fallback 逻辑是 CC-Switch 的 Skills SDK 强制要求的。所有 Skills 的execute()方法都必须处理MCPError、APIError、TokenLimitExceededError这三类异常并提供降级路径。否则 CI 会直接 fail。这就是治理的力量——把容错变成规范。4.4 生产监控从日志里挖出api error: the socket connection was closed unexpectedly的根因这个错误在网上搜90% 的答案是“重试”。但在我负责的金融风控 Agent 里它意味着上游 MCP Server 的内存泄漏。CC-Switch 的日志结构化做得极好每条日志都带 trace_id 和 component 标签[2024-06-15 14:22:31] [ERROR] [trace_idabc123] [componentmcp_client] Failed to call MCP method summarize: ConnectionResetError(104, Connection reset by peer) Caused by: File /cc-switch/mcp/client.py, line 87, in call response self.session.post(url, jsonpayload) File /requests/sessions.py, line 589, in post return self.request(POST, url, **kwargs)关键在Caused by后面的 stack trace。它指向mcp/client.py第 87 行而这一行正是self.session.post()。说明不是 Skills 代码的问题是 HTTP session 断了。再结合监控看playwright-mcp进程的 RSS 内存发现它每处理 100 个请求就涨 50MB3 小时后 OOM。根因是 Playwright 的page.close()没被正确调用。修复后socket connection closed错误下降了 99.7%。CC-Switch 不止是工具它是你的 Agent 系统的“黑匣子”。所有错误都被打上了精确的组件标签和调用链让你能像外科医生一样直接切到病灶。5. 生态扩展如何把 Codex、Figma、Wireshark 的 MCP Server 接入 CC-SwitchCC-Switch 的价值随着你接入的 MCP Server 数量呈指数增长。不是 112而是 1×2×36。我们来看三个真实案例Codex、Figma、Wireshark。5.1 Codex 接入为什么codex-app cc-switch deepseek是最强组合Codex 的 MCP Servercodex-mcp暴露的是 IDE 级别的能力get_file_content、edit_file、find_references。但 Codex 本身不支持 DeepSeek 这类外部模型。CC-Switch 就是那个“翻译官”。配置mcp/codex.yamlcodex-ide: type: mcp server_url: http://localhost:3000 capabilities: - get_file_content - edit_file - find_references - list_files schema: file://schemas/codex-mcp.json health_check: /health然后写一个 Skills让 DeepSeek “看懂” Codex 的文件结构# skills/code_analyzer.py class CodeAnalyzerSkill(Skill): def execute(self, file_path: str) - dict: # Step 1: 用 Codex 获取文件内容 codex get_mcp_client(codex-ide) content codex.call(get_file_content, {path: file_path}) # Step 2: 用 DeepSeek 分析 client get_api_client(deepseek-r1) response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: You are a senior Python architect. Analyze this code.}, {role: user, content: fFile: {file_path}\nContent:\n{content[content][:8000]}} # 防 overflow ] ) return {analysis: response.choices[0].message.content}这个 Skills 的威力在于它把 Codex 的“读写文件”能力和 DeepSeek 的“理解代码”能力无缝缝合。你不再需要在 VS Code 里复制粘贴代码到 ClaudeCC-Switch 自动完成。codex-app cc-switch deepseek这个组合让本地开发体验接近 GitHub Copilot Pro但数据完全不出内网。5.2 Figma MCP把设计稿变成可执行的前端代码Figma 的 MCP Serverfigma-mcp能导出设计稿的 JSON 结构。CC-Switch 让它和 Skills 联动实现“设计即代码”# mcp/figma.yaml figma-design: type: mcp server_url: https://figma-mcp.example.com capabilities: - get_page_json - get_component_json auth: bearer ${FIGMA_TOKEN}Skillsfigma_to_html.pydef execute(self, page_id: str) - dict: figma get_mcp_client(figma-design) # 获取设计稿 JSON page_json figma.call(get_page_json, {id: page_id}) # 调用一个专门的 Skills把 JSON 转成 HTML/CSS html_skill get_skill_client(json_to_html) html html_skill.execute(page_json) # 再用 DeepSeek 优化可访问性 client get_api_client(deepseek-r1) a11y_html client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: fAdd ARIA labels to this HTML: {html}}] ) return {html: a11y_html.choices[0].message.content}这就是figma mcp的真实价值它不是让你下载 PNG而是把设计语言变成可编程的、可 AI 增强的、可版本控制的代码资产。CC-Switch 是那个让设计和开发不再割裂的胶水。5.3 Wireshark MCP让网络工程师也能用上 LLMWireshark 的 MCP Serverwireshark-mcp能解析 pcap 文件返回结构化流量数据。这原本是网络工程师的专属领域。CC-Switch 让它平民化# mcp/wireshark.yaml network-trace: type: mcp server_url: http://wireshark-mcp:9000 capabilities: - parse_pcap - filter_packets - export_jsonSkillsthreat_hunter.pydef execute(self, pcap_path: str, threat_indicators: List[str]) - dict: ws get_mcp_client(network-trace) # 解析 pcap只取可疑流量 packets ws.call(filter_packets, { path: pcap_path, filter: tcp.port 443 http }) # 用 DeepSeek 分析 HTTP 流量找恶意行为 analysis get_api_client(deepseek-r1).chat.completions.create( modeldeepseek-chat, messages[{ role: user, content: fAnalyze these HTTP requests for C2 beaconing patterns. Indicators: {threat_indicators}. Packets: {packets[:5]} }] ) return {report: analysis.choices[0].message.content}wireshark mcp CC-Switch让一个不懂 Python 的安全分析师也能用自然语言问“这个 pcap 里有没有 Cobalt Strike 流量”——答案不再是 Wireshark 里密密麻麻的十六进制而是一份中文报告。这就是 MCP 协议和 CC-Switch 治理层共同释放的生产力。6. 经验之谈我在 12 个 Agent 项目里踩过的 7 个 CC-Switch 坑以及怎么绕过去理论再完美不如实战教训来得深刻。这是我过去半年在金融、电商、SaaS 三个行业的 12 个 Agent 项目里被 CC-Switch 教训出来的血泪经验。有些坑官方文档根本不会提。6.1 坑一cc-switch 配置完了之后在 claude 使用提示没有登录—— 这不是登录问题是 MCP 能力未声明这个错误信息极具误导性。它实际意思是“你试图在 Skills 里调用一个叫claude的 MCP Server但 CC-Switch 的配置里根本没有叫claude的 MCP”。Claude 官方不提供 MCP Server所以claude不是一个合法的 MCP name。正确做法把 Claude 当作一个 API而不是 MCP。配置在api/claude.yamlclaude-sonnet: type: anthropic api_key: ${ANTHROPIC_API_KEY} base_url: https://api.anthropic.com/v1然后在 Skills 里用get_api_client(claude-sonnet)而不是get_mcp_client(claude)。这个命名混淆是新手最大的认知陷阱。6.2 坑二api error: 402 insufficient balance—— 不是钱不够是 CC-Switch 的 rate limit 配置错了Anthropic 的 402 错误常被误认为余额不足。但如果你确认账户有钱那大概率是 CC-Switch 的rate_limit配置和 Anthropic 的 tier 不匹配。Anthropic 的messagesendpoint 有严格的 RPM每分钟请求数限制而 CC-Switch 的默认rate_limit: 60是按 OpenAI 的标准设的。修复方案在api/claude.yaml里显式声明claude-sonnet: # ... 其他配置 rate_limit: rpm: 5 # Anthropic 付费 tier 的 RPM 是 5 burst: 10CC-Switch 会用time.sleep()做精确的令牌桶限流。不加这个你的 Agent 会在高峰期被 Anthropic 的 429 拦住然后重试逻辑又触发 402形成雪崩。6.3 坑三cc-switch怎么如何添加agnes—— Agnes 不是插件是 CC-Switch 的内置 Skills 框架Agnes 是 CC-Switch 社区对“自动归档、归类、摘要”这一类 Skills 的统称不是某个叫agnes的第三方包。网上搜cc-switch 添加 agnes全是无效信息。正确做法自己写一个agnes.pySkills利用 CC-Switch 的cache和mcp能力class AgnesSkill(Skill): def execute(self, text: str, category: str) - dict: # Step 1: 用 cache 查重避免归档重复内容 cache_key fagnes:{hashlib.md5(text.encode()).hexdigest()} if self.cache.get(cache_key): return {status: duplicate, id: self.cache.get(cache_key)} # Step 2: 用 MCP 存储到向量库 vector_db get_mcp_client(qdrant-mcp) vector_db.call(upsert, {text: text, category: category}) # Step 3: 生成摘要并缓存 summary self._call_deepseek_summary(text) self.cache.set(cache_key, summary[id], ttl86400) return {status: archived, id: summary[id]}Agnes 的本质是 CC-Switch 的cachemcpapi三者协同的结果。把它当成一个框架而不是一个包。6.4 坑四mac 安装 cc-switch后command not found—— PATH 没生效Mac 上用