试了站里的几个方法还是有问题,新会话从来没有触发过自动压缩,Claude版本为2.1.162 (Claude Code) 我的settings.json配置如下: { “env”: { "API_TIMEOUT_MS": "3000000", "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-4.5-air", "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-5-turbo", "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.1", "ANTHROPIC_AUTH_TOKEN": "124214213432144234121234", "CLAUDE_CODE_DISABLE_1M_CONTEXT": "1", "ANTHROPIC_BASE_URL": "https://open.bigmodel.cn/api/anthropic", "CLAUDE_CODE_BLOCKING_LIMIT_OVERRIDE": "200000", "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "75" }, “model”: “GLM-5.1”, “effortLevel”: “medium”, “skipDangerousModePermissionPrompt”: true } 1 个帖子 - 1 位参与者 阅读完整话题
今天遇到这种情况:同一个项目老会话用着用着没法正常使用一直显示链接问题。但是新开会话又能正常使用,很奇怪 3 个帖子 - 3 位参与者 阅读完整话题
codex建立新会话之后,直接发送类似 codex-session-dialog xxxx-xxx-xxxx --last 10 就会获取指定session倒数10个的会话内容 #!/usr/bin/env python3 from __future__ import annotations import argparse import json import sqlite3 import sys from datetime import datetime, timezone from dataclasses import dataclass from pathlib import Path from typing import Iterable @dataclass class DialogMessage: role: str text: str @dataclass class SessionData: session_id: str | None messages: list[DialogMessage] cwd: str | None = None @dataclass class SessionMatch: file_path: Path session_id: str | None cwd: str | None sort_key: int | None = None title: str | None = None def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description='Extract a cleaned dialog transcript for a Codex session.' ) parser.add_argument('target', help='Session id/prefix, a jsonl file, or a workspace directory') parser.add_argument( '--codex-home', default=str(Path.home() / '.codex'), help='Codex home directory (default: ~/.codex)', ) parser.add_argument( '--include-environment', action='store_true', help='Keep <environment_context> user messages', ) parser.add_argument( '--last', type=int, default=None, help='Only output the most recent N dialog blocks, split by each user message.', ) parser.add_argument( '--list', action='store_true', help='List matching Codex sessions and their inferred titles for the given workspace path.', ) parser.add_argument( '--show-path', action='store_true', help='Show rollout jsonl path in list output.', ) return parser def normalize_text(text: str) -> str: lines = [line.rstrip() for line in text.replace('\r\n', '\n').replace('\r', '\n').split('\n')] compact = '\n'.join(lines).strip() while '\n\n\n' in compact: compact = compact.replace('\n\n\n', '\n\n') return compact def normalize_path_string(path_str: str) -> str: return str(Path(path_str).expanduser().resolve(strict=False)) def read_session_meta(path: Path) -> tuple[str | None, str | None]: try: with path.open(encoding='utf-8') as fh: first_line = fh.readline() payload = json.loads(first_line).get('payload') or {} except Exception: # noqa: BLE001 return None, None session_id = payload.get('id') cwd = payload.get('cwd') if not isinstance(session_id, str) or not session_id: session_id = None if not isinstance(cwd, str) or not cwd: cwd = None return session_id, cwd def iter_rollout_files(codex_home: Path) -> list[Path]: sessions_root = codex_home / 'sessions' if not sessions_root.exists(): raise FileNotFoundError(f'sessions directory not found: {sessions_root}') return sorted(sessions_root.rglob('rollout-*.jsonl')) def _find_session_file_by_id(session: str, codex_home: Path) -> Path: exact_matches: list[Path] = [] prefix_matches: list[Path] = [] for path in iter_rollout_files(codex_home): sid, _ = read_session_meta(path) if not sid: continue if sid == session: exact_matches.append(path) elif sid.startswith(session): prefix_matches.append(path) if len(exact_matches) == 1: return exact_matches[0] if len(exact_matches) > 1: raise ValueError(f'multiple exact matches found for session {session}') if len(prefix_matches) == 1: return prefix_matches[0] if not prefix_matches: raise FileNotFoundError(f'no session file found for {session}') raise ValueError(f'multiple prefix matches found for {session}') def find_sessions_by_workspace(workspace: Path, codex_home: Path) -> list[SessionMatch]: workspace_key = normalize_path_string(str(workspace)) matches: list[SessionMatch] = [] for path in iter_rollout_files(codex_home): session_id, cwd = read_session_meta(path) if not cwd: continue if normalize_path_string(cwd) == workspace_key: matches.append(SessionMatch(file_path=path, session_id=session_id, cwd=cwd)) if not matches: raise FileNotFoundError(f'no Codex sessions found for workspace {workspace}') for match in matches: match.sort_key = read_thread_sort_key(codex_home, match) matches.sort( key=lambda item: ( item.sort_key if item.sort_key is not None else int(item.file_path.stat().st_mtime * 1000), str(item.file_path), ) ) return matches def resolve_target(target: str, codex_home: Path) -> Path: candidate = Path(target).expanduser() if candidate.is_file(): if candidate.suffix.lower() != '.jsonl': raise ValueError(f'not a jsonl file: {candidate}') return candidate if candidate.is_dir(): matches = find_sessions_by_workspace(candidate, codex_home) non_empty: list[SessionMatch] = [] for match in matches: session_data = load_session_messages(match.file_path) if not is_effectively_empty_thread(codex_home, match, session_data): non_empty.append(match) if not non_empty: raise FileNotFoundError(f'no non-empty Codex sessions found for workspace {candidate}') return non_empty[-1].file_path return _find_session_file_by_id(target, codex_home) def extract_text_parts(content_items: Iterable[dict]) -> str: parts: list[str] = [] for item in content_items: if not isinstance(item, dict): continue text = item.get('text') if isinstance(text, str) and text.strip(): parts.append(text) continue output = item.get('output') if isinstance(output, str) and output.strip(): parts.append(output) return normalize_text('\n\n'.join(parts)) def should_skip(role: str, text: str, include_environment: bool) -> bool: if role not in {'user', 'assistant'}: return True if not text: return True if not include_environment and text.startswith('<environment_context>'): return True return False def load_session_messages(path: Path, include_environment: bool = False) -> SessionData: session_id: str | None = None session_cwd: str | None = None messages: list[DialogMessage] = [] last_key: tuple[str, str] | None = None with path.open(encoding='utf-8') as fh: for line in fh: row = json.loads(line) row_type = row.get('type') payload = row.get('payload') or {} if row_type == 'session_meta' and isinstance(payload, dict): value = payload.get('id') cwd = payload.get('cwd') if isinstance(value, str) and value: session_id = value if isinstance(cwd, str) and cwd: session_cwd = cwd continue if row_type != 'response_item' or not isinstance(payload, dict): continue if payload.get('type') != 'message': continue role = payload.get('role') text = extract_text_parts(payload.get('content') or []) if should_skip(role, text, include_environment): continue key = (role, normalize_text(text)) if key == last_key: continue last_key = key messages.append(DialogMessage(role=role, text=text)) return SessionData(session_id=session_id, messages=messages, cwd=session_cwd) def group_dialogs(messages: list[DialogMessage]) -> list[list[DialogMessage]]: dialogs: list[list[DialogMessage]] = [] current: list[DialogMessage] = [] for message in messages: if message.role == 'user': if current: dialogs.append(current) current = [message] continue if current: current.append(message) if current: dialogs.append(current) return dialogs def infer_title(session_data: SessionData, path: Path) -> str: for message in session_data.messages: if message.role == 'user' and message.text: return message.text.splitlines()[0] return path.stem def read_thread_row(codex_home: Path, match: SessionMatch, session_data: SessionData | None = None) -> sqlite3.Row | None: db_path = codex_home / 'state_5.sqlite' if not db_path.exists(): return None try: conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row row = conn.execute( "select * from threads where rollout_path = ? limit 1", (str(match.file_path),), ).fetchone() if row is None: sid = None if session_data and session_data.session_id: sid = session_data.session_id elif match.session_id: sid = match.session_id if sid: row = conn.execute( "select * from threads where id = ? limit 1", (sid,), ).fetchone() conn.close() return row except Exception: # noqa: BLE001 return None def read_thread_sort_key(codex_home: Path, match: SessionMatch) -> int | None: row = read_thread_row(codex_home, match) if row is None: return None keys = row.keys() if 'updated_at_ms' in keys: value = row['updated_at_ms'] if isinstance(value, int): return value if 'updated_at' in keys: value = row['updated_at'] if isinstance(value, int): return value * 1000 return None def read_resume_title(codex_home: Path, match: SessionMatch, session_data: SessionData) -> str | None: row = read_thread_row(codex_home, match, session_data) if row is None: return None title = row['title'] return title if isinstance(title, str) and title else None def format_sort_key(sort_key: int | None, fallback_path: Path) -> str: if sort_key is not None: return datetime.fromtimestamp(sort_key / 1000, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%SZ') return datetime.fromtimestamp(fallback_path.stat().st_mtime, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%SZ') def truncate_title(title: str, max_len: int = 60) -> str: if len(title) <= max_len: return title return title[: max_len - 3] + '...' def is_effectively_empty_thread(codex_home: Path, match: SessionMatch, session_data: SessionData) -> bool: title = read_resume_title(codex_home, match, session_data) if title: match.title = title return False return len(session_data.messages) == 0 def render_list_output(target_path: Path, codex_home: Path, include_environment: bool, show_path: bool = False) -> str: lines = [f'# workspace: {target_path}', ''] if target_path.is_file(): matches = [SessionMatch(file_path=target_path, session_id=None, cwd=None)] elif target_path.is_dir(): matches = find_sessions_by_workspace(target_path, codex_home) else: raise FileNotFoundError(f'path not found: {target_path}') for match in matches: session_data = load_session_messages(match.file_path, include_environment=include_environment) if is_effectively_empty_thread(codex_home, match, session_data): continue raw_title = match.title or infer_title(session_data, match.file_path) title = truncate_title(raw_title) session_id = session_data.session_id or match.session_id or '-' updated = format_sort_key(match.sort_key, match.file_path) base = f'{updated} | {session_id} | {title}' lines.append(f'{base} | {match.file_path}' if show_path else base) return '\n'.join(lines).rstrip() + '\n' def render_output( session_id: str | None, path: Path, messages: list[DialogMessage], last: int | None = None, ) -> str: actual_session = session_id or path.stem lines = [f'# session: {actual_session}', f'# file: {path}', ''] if last is None: for message in messages: lines.append(f'{message.role}: {message.text}') return '\n'.join(lines).rstrip() + '\n' dialogs = group_dialogs(messages) if last < 0: raise ValueError('--last must be >= 0') selected = dialogs[-last:] if last else [] for index, dialog in enumerate(selected, 1): lines.append(f'## dialog {index}') for message in dialog: lines.append(f'{message.role}: {message.text}') lines.append('') return '\n'.join(lines).rstrip() + '\n' def main() -> int: parser = build_parser() args = parser.parse_args() try: codex_home = Path(args.codex_home).expanduser() target_path = Path(args.target).expanduser() if args.list: output = render_list_output( target_path, codex_home=codex_home, include_environment=args.include_environment, show_path=args.show_path, ) else: path = resolve_target(args.target, codex_home) session_data = load_session_messages( path, include_environment=args.include_environment ) output = render_output( session_data.session_id, path, session_data.messages, last=args.last, ) except Exception as exc: # noqa: BLE001 print(f'error: {exc}', file=sys.stderr) return 1 sys.stdout.write(output) return 0 if __name__ == '__main__': raise SystemExit(main()) 1 个帖子 - 1 位参与者 阅读完整话题
问题:每次新会话,AI 从零开始 在使用 Claude Code 这类 AI 编程助手时,我们通常依赖 CLAUDE.md 和 .claude/ 文件夹来帮助 AI 记住项目的上下文——约定、架构、进度、决策。 但这种方法有几个根本性的问题: 记忆是静态的 :你手动写进文档的内容,不会随着项目进展自动更新。项目做了两周, CLAUDE.md 可能还是最初的版本。 决策是断裂的 :AI 在某个会话中做了"选 PostgreSQL 而非 MySQL"的决策,但下一个会话看不到这个决策——除非你手动写进文档。 上下文是孤立的 :AI 知道当前要做什么,但不知道"为什么这样做"——上游做了什么决策、有哪些约束,这些信息丢失了。 文档会膨胀 :随着项目推进,记忆文件越来越长,信息密度越来越低,AI 和人都难以从中找到关键信息。 核心矛盾: 项目是一个持续演进的过程,但 AI 的记忆是一个需要手动维护的快照。 想法:用事件树做项目记忆 如果我们不用文档,而是用一棵 带依赖关系的事件树 来记录项目呢? 构建用户认证系统(项目目标) │ ├── 技术选型 ✅ done │ └── outcome: 选了 Rust + SQLite ,团队熟悉且单机部署足够 │ │ │ ├── 数据库设计 ✅ done │ │ └── outcome: 用户表 + 角色表 + 权限表,RBAC 模型 │ │ │ │ │ └── 实现 JWT 认证 ◐ doing ← 当前任务 │ │ │ └── API 网关设计 ○ todo │ └── 部署方案 ○ todo 每个事件有状态( todo / doing / done / abandoned ),事件之间有依赖关系。当一个事件完成时,记录它的 决策结果( outcome ) ——为什么这样做、选了什么、有什么约束。 关键机制: 当你查看某个事件时,系统沿依赖链向上遍历,把所有已完成事件的 outcome 自动组装成上下文。 AI 不需要翻文档、不需要人手动维护记忆。它只需要问"下一步做什么",就能拿到: 当前任务是什么 上游做了哪些决策、为什么 有哪些约束条件 这棵树能做什么 跨会话的决策连续性 AI 编程助手每次新会话都从零开始,但项目不是从零开始的。事件树的核心能力是 把上游的决策结果自动注入到当前任务的上下文中 。 当一个 AI 会话开始工作前,它问系统"下一步做什么",系统不仅告诉它当前该做哪个任务,还会沿着依赖链把前面所有已完成事件的决策结果组装在一起——选了什么技术、为什么这样设计、有哪些约束。AI 拿到的不是一个孤立的任务标题,而是站在一系列已验证的决策之上开始工作。当这个会话结束时,它把自己的决策结果写回事件树,下一个会话就能看到。决策链就这样跨会话地生长。 位置感知 在大型项目中,每个人(或每个 AI 会话)通常只负责一个局部。事件树让执行者看到自己在整体中的位置——项目的最终目标是什么,我当前的任务在哪个节点上,上游做了哪些决策把我带到了这里,下游还有哪些任务依赖我的产出。这不是简单的"待办清单",而是 带方向感的地图 。 计划的弹性 项目过程中计划会变。事件可以被废弃,废弃时记录原因。被废弃的事件不影响依赖链的推进(下游事件仍然可以继续),但原因会被保留在历史中。AI 知道"曾经考虑过这个方案但放弃了",不会在未来的会话中重复提议已经否决的方向。依赖关系本身也可以被调整——新增依赖、移除依赖、拆分事件——树的结构跟着项目的理解一起演化,而不是一开始就固定不变。它可能是一个完整的计划,执行一半中途修改计划,也可能只规划了大方向,细枝末节完全没有计划,但都适用。 对人也有用 不只是给 AI 用的工具。一个人的项目也可以用它来记录想法、理清顺序、记住当时的思考过程。三五天后回来, list 一看就知道有什么没做完, show 一下就能回想起当时为什么想做这件事。 畅想:自动执行循环 上面描述的是人手动推进事件树的流程——人(或 AI 会话)问"下一步做什么",拿到任务和上下文,执行完后手动写回结果。 但如果把这件事自动化呢? 设想这样一个循环:系统查看事件树,找到下一个可执行的任务,把任务描述和完整的决策链上下文打包,发送给执行器(一个 AI Agent ,比如 Claude Code 会话)。执行器拿到这些信息后开始工作——写代码、跑测试、解决问题。完成后,执行器把执行结果(做了什么决策、选了什么方案、遇到什么问题)反馈回来,系统自动更新事件树的状态和 outcome 。 然后系统再查看事件树。刚刚完成的任务可能解锁了新的可执行事件——原本因为依赖它而等待的任务现在就绪了。系统再次找到下一个任务,再次组装上下文,再次调用执行器。 事件树 ──查看下一步──→ 组装上下文 ──→ 调用执行器 │ 执行器完成任务 │ 事件树 ◀──更新状态── 接收反馈 ◀─────┘ │ └── 新任务就绪?──→ 是 ──→ 继续循环 否 ──→ 等待或通知人介入 在这个循环中,事件树扮演的不是"待办清单",而是 项目的状态机 。它知道哪些任务已经完成、哪些因为依赖而就绪、哪些因为上游未完成而被阻断。执行器不需要知道项目全貌,它只需要拿到当前任务的上下文(决策链),专注于执行。事件树负责记忆和调度。 更有意思的是,执行器的反馈不只是"完成了"。如果执行器发现当前任务的某个前提假设不成立,或者需要拆分成更细的子任务,或者发现一个更好的方向需要废弃原计划——这些信息反馈回事件树,树的结构就会相应调整。树在生长,不只是在推进。 人在这个循环中的角色从"逐步推进每个任务"变成"设定目标和方向,监控进展,在需要判断时介入"。日常的执行、记忆维护、上下文传递,全部自动化。 当然,这还很远。要让这个循环真正跑起来,需要解决很多问题:执行器如何可靠地反馈结构化信息、系统如何判断反馈质量、什么时候需要人介入、如何处理执行失败。但这棵树的结构——带依赖的事件、自动组装的决策链、状态的流转——天然地支持这个方向。 现在的实现 目前 vibecoding 了一版勉强可用的 knit ,核心是一个 Rust CLI 工具: 核心循环: knit next --json → 读取记忆(拿到下一步 + 决策链上下文) 执行任务 knit report done --outcome "..." → 写入记忆(记录决策) 循环 10 个命令覆盖了创建、查看、编辑、报告、推荐、废弃等操作。全部输出支持 --json ,AI 可以程序化消费。 存储是单文件 SQLite ,不需要服务器。LLM 推荐是可选的( knit next 可以配置 LLM 做智能排序,也可以不配置,用简单的依赖出度排序)。 事件树的 BFS 遍历自动沿依赖链收集决策链,钻石依赖会去重,被阻断的事件(上游还有未完成依赖)会标记 blocked 。废弃的事件被跳过,但原因保留。 SO ? 不知道大家怎么看这样的一个设计,我也不知道是否有一定的可行性,它看起来适合做很多方面的工作,也可以考虑它只为 agent 的项目记忆服务,目前的定位其实也有点模糊。 如果你有兴趣看看项目,里面的文档有更多信息 qkyufw/knit 欢迎评论
问题:每次新会话,AI 从零开始 在使用 Claude Code 这类 AI 编程助手时,我们通常依赖 CLAUDE.md 和 .claude/ 文件夹来帮助 AI 记住项目的上下文——约定、架构、进度、决策。 但这种方法有几个根本性的问题: 记忆是静态的 :你手动写进文档的内容,不会随着项目进展自动更新。项目做了两周, CLAUDE.md 可能还是最初的版本。 决策是断裂的 :AI 在某个会话中做了"选 PostgreSQL 而非 MySQL"的决策,但下一个会话看不到这个决策——除非你手动写进文档。 上下文是孤立的 :AI 知道当前要做什么,但不知道"为什么这样做"——上游做了什么决策、有哪些约束,这些信息丢失了。 文档会膨胀 :随着项目推进,记忆文件越来越长,信息密度越来越低,AI 和人都难以从中找到关键信息。 核心矛盾: 项目是一个持续演进的过程,但 AI 的记忆是一个需要手动维护的快照。 想法:用事件树做项目记忆 如果我们不用文档,而是用一棵 带依赖关系的事件树 来记录项目呢? 构建用户认证系统(项目目标) │ ├── 技术选型 ✅ done │ └── outcome: 选了 Rust + SQLite ,团队熟悉且单机部署足够 │ │ │ ├── 数据库设计 ✅ done │ │ └── outcome: 用户表 + 角色表 + 权限表,RBAC 模型 │ │ │ │ │ └── 实现 JWT 认证 ◐ doing ← 当前任务 │ │ │ └── API 网关设计 ○ todo │ └── 部署方案 ○ todo 每个事件有状态( todo / doing / done / abandoned ),事件之间有依赖关系。当一个事件完成时,记录它的 决策结果( outcome ) ——为什么这样做、选了什么、有什么约束。 关键机制: 当你查看某个事件时,系统沿依赖链向上遍历,把所有已完成事件的 outcome 自动组装成上下文。 AI 不需要翻文档、不需要人手动维护记忆。它只需要问"下一步做什么",就能拿到: 当前任务是什么 上游做了哪些决策、为什么 有哪些约束条件 这棵树能做什么 跨会话的决策连续性 AI 编程助手每次新会话都从零开始,但项目不是从零开始的。事件树的核心能力是 把上游的决策结果自动注入到当前任务的上下文中 。 当一个 AI 会话开始工作前,它问系统"下一步做什么",系统不仅告诉它当前该做哪个任务,还会沿着依赖链把前面所有已完成事件的决策结果组装在一起——选了什么技术、为什么这样设计、有哪些约束。AI 拿到的不是一个孤立的任务标题,而是站在一系列已验证的决策之上开始工作。当这个会话结束时,它把自己的决策结果写回事件树,下一个会话就能看到。决策链就这样跨会话地生长。 位置感知 在大型项目中,每个人(或每个 AI 会话)通常只负责一个局部。事件树让执行者看到自己在整体中的位置——项目的最终目标是什么,我当前的任务在哪个节点上,上游做了哪些决策把我带到了这里,下游还有哪些任务依赖我的产出。这不是简单的"待办清单",而是 带方向感的地图 。 计划的弹性 项目过程中计划会变。事件可以被废弃,废弃时记录原因。被废弃的事件不影响依赖链的推进(下游事件仍然可以继续),但原因会被保留在历史中。AI 知道"曾经考虑过这个方案但放弃了",不会在未来的会话中重复提议已经否决的方向。依赖关系本身也可以被调整——新增依赖、移除依赖、拆分事件——树的结构跟着项目的理解一起演化,而不是一开始就固定不变。它可能是一个完整的计划,执行一半中途修改计划,也可能只规划了大方向,细枝末节完全没有计划,但都适用。 对人也有用 不只是给 AI 用的工具。一个人的项目也可以用它来记录想法、理清顺序、记住当时的思考过程。三五天后回来, list 一看就知道有什么没做完, show 一下就能回想起当时为什么想做这件事。 畅想:自动执行循环 上面描述的是人手动推进事件树的流程——人(或 AI 会话)问"下一步做什么",拿到任务和上下文,执行完后手动写回结果。 但如果把这件事自动化呢? 设想这样一个循环:系统查看事件树,找到下一个可执行的任务,把任务描述和完整的决策链上下文打包,发送给执行器(一个 AI Agent ,比如 Claude Code 会话)。执行器拿到这些信息后开始工作——写代码、跑测试、解决问题。完成后,执行器把执行结果(做了什么决策、选了什么方案、遇到什么问题)反馈回来,系统自动更新事件树的状态和 outcome 。 然后系统再查看事件树。刚刚完成的任务可能解锁了新的可执行事件——原本因为依赖它而等待的任务现在就绪了。系统再次找到下一个任务,再次组装上下文,再次调用执行器。 事件树 ──查看下一步──→ 组装上下文 ──→ 调用执行器 │ 执行器完成任务 │ 事件树 ◀──更新状态── 接收反馈 ◀─────┘ │ └── 新任务就绪?──→ 是 ──→ 继续循环 否 ──→ 等待或通知人介入 在这个循环中,事件树扮演的不是"待办清单",而是 项目的状态机 。它知道哪些任务已经完成、哪些因为依赖而就绪、哪些因为上游未完成而被阻断。执行器不需要知道项目全貌,它只需要拿到当前任务的上下文(决策链),专注于执行。事件树负责记忆和调度。 更有意思的是,执行器的反馈不只是"完成了"。如果执行器发现当前任务的某个前提假设不成立,或者需要拆分成更细的子任务,或者发现一个更好的方向需要废弃原计划——这些信息反馈回事件树,树的结构就会相应调整。树在生长,不只是在推进。 人在这个循环中的角色从"逐步推进每个任务"变成"设定目标和方向,监控进展,在需要判断时介入"。日常的执行、记忆维护、上下文传递,全部自动化。 当然,这还很远。要让这个循环真正跑起来,需要解决很多问题:执行器如何可靠地反馈结构化信息、系统如何判断反馈质量、什么时候需要人介入、如何处理执行失败。但这棵树的结构——带依赖的事件、自动组装的决策链、状态的流转——天然地支持这个方向。 现在的实现 目前 vibecoding 了一版勉强可用的 knit ,核心是一个 Rust CLI 工具: 核心循环: knit next --json → 读取记忆(拿到下一步 + 决策链上下文) 执行任务 knit report done --outcome "..." → 写入记忆(记录决策) 循环 10 个命令覆盖了创建、查看、编辑、报告、推荐、废弃等操作。全部输出支持 --json ,AI 可以程序化消费。 存储是单文件 SQLite ,不需要服务器。LLM 推荐是可选的( knit next 可以配置 LLM 做智能排序,也可以不配置,用简单的依赖出度排序)。 事件树的 BFS 遍历自动沿依赖链收集决策链,钻石依赖会去重,被阻断的事件(上游还有未完成依赖)会标记 blocked 。废弃的事件被跳过,但原因保留。 SO ? 不知道大家怎么看这样的一个设计,我也不知道是否有一定的可行性,它看起来适合做很多方面的工作,也可以考虑它只为 agent 的项目记忆服务,目前的定位其实也有点模糊。 如果你有兴趣看看项目,里面的文档有更多信息 qkyufw/knit 欢迎评论
问题:每次新会话,AI 从零开始 在使用 Claude Code 这类 AI 编程助手时,我们通常依赖 CLAUDE.md 和 .claude/ 文件夹来帮助 AI 记住项目的上下文——约定、架构、进度、决策。 但这种方法有几个根本性的问题: 记忆是静态的 :你手动写进文档的内容,不会随着项目进展自动更新。项目做了两周, CLAUDE.md 可能还是最初的版本。 决策是断裂的 :AI 在某个会话中做了"选 PostgreSQL 而非 MySQL"的决策,但下一个会话看不到这个决策——除非你手动写进文档。 上下文是孤立的 :AI 知道当前要做什么,但不知道"为什么这样做"——上游做了什么决策、有哪些约束,这些信息丢失了。 文档会膨胀 :随着项目推进,记忆文件越来越长,信息密度越来越低,AI 和人都难以从中找到关键信息。 核心矛盾: 项目是一个持续演进的过程,但 AI 的记忆是一个需要手动维护的快照。 想法:用事件树做项目记忆 如果我们不用文档,而是用一棵 带依赖关系的事件树 来记录项目呢? 构建用户认证系统(项目目标) │ ├── 技术选型 ✅ done │ └── outcome: 选了 Rust + SQLite ,团队熟悉且单机部署足够 │ │ │ ├── 数据库设计 ✅ done │ │ └── outcome: 用户表 + 角色表 + 权限表,RBAC 模型 │ │ │ │ │ └── 实现 JWT 认证 ◐ doing ← 当前任务 │ │ │ └── API 网关设计 ○ todo │ └── 部署方案 ○ todo 每个事件有状态( todo / doing / done / abandoned ),事件之间有依赖关系。当一个事件完成时,记录它的 决策结果( outcome ) ——为什么这样做、选了什么、有什么约束。 关键机制: 当你查看某个事件时,系统沿依赖链向上遍历,把所有已完成事件的 outcome 自动组装成上下文。 AI 不需要翻文档、不需要人手动维护记忆。它只需要问"下一步做什么",就能拿到: 当前任务是什么 上游做了哪些决策、为什么 有哪些约束条件 这棵树能做什么 跨会话的决策连续性 AI 编程助手每次新会话都从零开始,但项目不是从零开始的。事件树的核心能力是 把上游的决策结果自动注入到当前任务的上下文中 。 当一个 AI 会话开始工作前,它问系统"下一步做什么",系统不仅告诉它当前该做哪个任务,还会沿着依赖链把前面所有已完成事件的决策结果组装在一起——选了什么技术、为什么这样设计、有哪些约束。AI 拿到的不是一个孤立的任务标题,而是站在一系列已验证的决策之上开始工作。当这个会话结束时,它把自己的决策结果写回事件树,下一个会话就能看到。决策链就这样跨会话地生长。 位置感知 在大型项目中,每个人(或每个 AI 会话)通常只负责一个局部。事件树让执行者看到自己在整体中的位置——项目的最终目标是什么,我当前的任务在哪个节点上,上游做了哪些决策把我带到了这里,下游还有哪些任务依赖我的产出。这不是简单的"待办清单",而是 带方向感的地图 。 计划的弹性 项目过程中计划会变。事件可以被废弃,废弃时记录原因。被废弃的事件不影响依赖链的推进(下游事件仍然可以继续),但原因会被保留在历史中。AI 知道"曾经考虑过这个方案但放弃了",不会在未来的会话中重复提议已经否决的方向。依赖关系本身也可以被调整——新增依赖、移除依赖、拆分事件——树的结构跟着项目的理解一起演化,而不是一开始就固定不变。它可能是一个完整的计划,执行一半中途修改计划,也可能只规划了大方向,细枝末节完全没有计划,但都适用。 对人也有用 不只是给 AI 用的工具。一个人的项目也可以用它来记录想法、理清顺序、记住当时的思考过程。三五天后回来, list 一看就知道有什么没做完, show 一下就能回想起当时为什么想做这件事。 畅想:自动执行循环 上面描述的是人手动推进事件树的流程——人(或 AI 会话)问"下一步做什么",拿到任务和上下文,执行完后手动写回结果。 但如果把这件事自动化呢? 设想这样一个循环:系统查看事件树,找到下一个可执行的任务,把任务描述和完整的决策链上下文打包,发送给执行器(一个 AI Agent ,比如 Claude Code 会话)。执行器拿到这些信息后开始工作——写代码、跑测试、解决问题。完成后,执行器把执行结果(做了什么决策、选了什么方案、遇到什么问题)反馈回来,系统自动更新事件树的状态和 outcome 。 然后系统再查看事件树。刚刚完成的任务可能解锁了新的可执行事件——原本因为依赖它而等待的任务现在就绪了。系统再次找到下一个任务,再次组装上下文,再次调用执行器。 事件树 ──查看下一步──→ 组装上下文 ──→ 调用执行器 │ 执行器完成任务 │ 事件树 ◀──更新状态── 接收反馈 ◀─────┘ │ └── 新任务就绪?──→ 是 ──→ 继续循环 否 ──→ 等待或通知人介入 在这个循环中,事件树扮演的不是"待办清单",而是 项目的状态机 。它知道哪些任务已经完成、哪些因为依赖而就绪、哪些因为上游未完成而被阻断。执行器不需要知道项目全貌,它只需要拿到当前任务的上下文(决策链),专注于执行。事件树负责记忆和调度。 更有意思的是,执行器的反馈不只是"完成了"。如果执行器发现当前任务的某个前提假设不成立,或者需要拆分成更细的子任务,或者发现一个更好的方向需要废弃原计划——这些信息反馈回事件树,树的结构就会相应调整。树在生长,不只是在推进。 人在这个循环中的角色从"逐步推进每个任务"变成"设定目标和方向,监控进展,在需要判断时介入"。日常的执行、记忆维护、上下文传递,全部自动化。 当然,这还很远。要让这个循环真正跑起来,需要解决很多问题:执行器如何可靠地反馈结构化信息、系统如何判断反馈质量、什么时候需要人介入、如何处理执行失败。但这棵树的结构——带依赖的事件、自动组装的决策链、状态的流转——天然地支持这个方向。 现在的实现 目前 vibecoding 了一版勉强可用的 knit ,核心是一个 Rust CLI 工具: 核心循环: knit next --json → 读取记忆(拿到下一步 + 决策链上下文) 执行任务 knit report done --outcome "..." → 写入记忆(记录决策) 循环 10 个命令覆盖了创建、查看、编辑、报告、推荐、废弃等操作。全部输出支持 --json ,AI 可以程序化消费。 存储是单文件 SQLite ,不需要服务器。LLM 推荐是可选的( knit next 可以配置 LLM 做智能排序,也可以不配置,用简单的依赖出度排序)。 事件树的 BFS 遍历自动沿依赖链收集决策链,钻石依赖会去重,被阻断的事件(上游还有未完成依赖)会标记 blocked 。废弃的事件被跳过,但原因保留。 SO ? 不知道大家怎么看这样的一个设计,我也不知道是否有一定的可行性,它看起来适合做很多方面的工作,也可以考虑它只为 agent 的项目记忆服务,目前的定位其实也有点模糊。 如果你有兴趣看看项目,里面的文档有更多信息 qkyufw/knit 欢迎评论
问题:每次新会话,AI 从零开始 在使用 Claude Code 这类 AI 编程助手时,我们通常依赖 CLAUDE.md 和 .claude/ 文件夹来帮助 AI 记住项目的上下文——约定、架构、进度、决策。 但这种方法有几个根本性的问题: 记忆是静态的 :你手动写进文档的内容,不会随着项目进展自动更新。项目做了两周, CLAUDE.md 可能还是最初的版本。 决策是断裂的 :AI 在某个会话中做了"选 PostgreSQL 而非 MySQL"的决策,但下一个会话看不到这个决策——除非你手动写进文档。 上下文是孤立的 :AI 知道当前要做什么,但不知道"为什么这样做"——上游做了什么决策、有哪些约束,这些信息丢失了。 文档会膨胀 :随着项目推进,记忆文件越来越长,信息密度越来越低,AI 和人都难以从中找到关键信息。 核心矛盾: 项目是一个持续演进的过程,但 AI 的记忆是一个需要手动维护的快照。 想法:用事件树做项目记忆 如果我们不用文档,而是用一棵 带依赖关系的事件树 来记录项目呢? 构建用户认证系统(项目目标) │ ├── 技术选型 ✅ done │ └── outcome: 选了 Rust + SQLite ,团队熟悉且单机部署足够 │ │ │ ├── 数据库设计 ✅ done │ │ └── outcome: 用户表 + 角色表 + 权限表,RBAC 模型 │ │ │ │ │ └── 实现 JWT 认证 ◐ doing ← 当前任务 │ │ │ └── API 网关设计 ○ todo │ └── 部署方案 ○ todo 每个事件有状态( todo / doing / done / abandoned ),事件之间有依赖关系。当一个事件完成时,记录它的 决策结果( outcome ) ——为什么这样做、选了什么、有什么约束。 关键机制: 当你查看某个事件时,系统沿依赖链向上遍历,把所有已完成事件的 outcome 自动组装成上下文。 AI 不需要翻文档、不需要人手动维护记忆。它只需要问"下一步做什么",就能拿到: 当前任务是什么 上游做了哪些决策、为什么 有哪些约束条件 这棵树能做什么 跨会话的决策连续性 AI 编程助手每次新会话都从零开始,但项目不是从零开始的。事件树的核心能力是 把上游的决策结果自动注入到当前任务的上下文中 。 当一个 AI 会话开始工作前,它问系统"下一步做什么",系统不仅告诉它当前该做哪个任务,还会沿着依赖链把前面所有已完成事件的决策结果组装在一起——选了什么技术、为什么这样设计、有哪些约束。AI 拿到的不是一个孤立的任务标题,而是站在一系列已验证的决策之上开始工作。当这个会话结束时,它把自己的决策结果写回事件树,下一个会话就能看到。决策链就这样跨会话地生长。 位置感知 在大型项目中,每个人(或每个 AI 会话)通常只负责一个局部。事件树让执行者看到自己在整体中的位置——项目的最终目标是什么,我当前的任务在哪个节点上,上游做了哪些决策把我带到了这里,下游还有哪些任务依赖我的产出。这不是简单的"待办清单",而是 带方向感的地图 。 计划的弹性 项目过程中计划会变。事件可以被废弃,废弃时记录原因。被废弃的事件不影响依赖链的推进(下游事件仍然可以继续),但原因会被保留在历史中。AI 知道"曾经考虑过这个方案但放弃了",不会在未来的会话中重复提议已经否决的方向。依赖关系本身也可以被调整——新增依赖、移除依赖、拆分事件——树的结构跟着项目的理解一起演化,而不是一开始就固定不变。它可能是一个完整的计划,执行一半中途修改计划,也可能只规划了大方向,细枝末节完全没有计划,但都适用。 对人也有用 不只是给 AI 用的工具。一个人的项目也可以用它来记录想法、理清顺序、记住当时的思考过程。三五天后回来, list 一看就知道有什么没做完, show 一下就能回想起当时为什么想做这件事。 畅想:自动执行循环 上面描述的是人手动推进事件树的流程——人(或 AI 会话)问"下一步做什么",拿到任务和上下文,执行完后手动写回结果。 但如果把这件事自动化呢? 设想这样一个循环:系统查看事件树,找到下一个可执行的任务,把任务描述和完整的决策链上下文打包,发送给执行器(一个 AI Agent ,比如 Claude Code 会话)。执行器拿到这些信息后开始工作——写代码、跑测试、解决问题。完成后,执行器把执行结果(做了什么决策、选了什么方案、遇到什么问题)反馈回来,系统自动更新事件树的状态和 outcome 。 然后系统再查看事件树。刚刚完成的任务可能解锁了新的可执行事件——原本因为依赖它而等待的任务现在就绪了。系统再次找到下一个任务,再次组装上下文,再次调用执行器。 事件树 ──查看下一步──→ 组装上下文 ──→ 调用执行器 │ 执行器完成任务 │ 事件树 ◀──更新状态── 接收反馈 ◀─────┘ │ └── 新任务就绪?──→ 是 ──→ 继续循环 否 ──→ 等待或通知人介入 在这个循环中,事件树扮演的不是"待办清单",而是 项目的状态机 。它知道哪些任务已经完成、哪些因为依赖而就绪、哪些因为上游未完成而被阻断。执行器不需要知道项目全貌,它只需要拿到当前任务的上下文(决策链),专注于执行。事件树负责记忆和调度。 更有意思的是,执行器的反馈不只是"完成了"。如果执行器发现当前任务的某个前提假设不成立,或者需要拆分成更细的子任务,或者发现一个更好的方向需要废弃原计划——这些信息反馈回事件树,树的结构就会相应调整。树在生长,不只是在推进。 人在这个循环中的角色从"逐步推进每个任务"变成"设定目标和方向,监控进展,在需要判断时介入"。日常的执行、记忆维护、上下文传递,全部自动化。 当然,这还很远。要让这个循环真正跑起来,需要解决很多问题:执行器如何可靠地反馈结构化信息、系统如何判断反馈质量、什么时候需要人介入、如何处理执行失败。但这棵树的结构——带依赖的事件、自动组装的决策链、状态的流转——天然地支持这个方向。 现在的实现 目前 vibecoding 了一版勉强可用的 knit ,核心是一个 Rust CLI 工具: 核心循环: knit next --json → 读取记忆(拿到下一步 + 决策链上下文) 执行任务 knit report done --outcome "..." → 写入记忆(记录决策) 循环 10 个命令覆盖了创建、查看、编辑、报告、推荐、废弃等操作。全部输出支持 --json ,AI 可以程序化消费。 存储是单文件 SQLite ,不需要服务器。LLM 推荐是可选的( knit next 可以配置 LLM 做智能排序,也可以不配置,用简单的依赖出度排序)。 事件树的 BFS 遍历自动沿依赖链收集决策链,钻石依赖会去重,被阻断的事件(上游还有未完成依赖)会标记 blocked 。废弃的事件被跳过,但原因保留。 SO ? 不知道大家怎么看这样的一个设计,我也不知道是否有一定的可行性,它看起来适合做很多方面的工作,也可以考虑它只为 agent 的项目记忆服务,目前的定位其实也有点模糊。 如果你有兴趣看看项目,里面的文档有更多信息 qkyufw/knit 欢迎评论
问题:每次新会话,AI 从零开始 在使用 Claude Code 这类 AI 编程助手时,我们通常依赖 CLAUDE.md 和 .claude/ 文件夹来帮助 AI 记住项目的上下文——约定、架构、进度、决策。 但这种方法有几个根本性的问题: 记忆是静态的 :你手动写进文档的内容,不会随着项目进展自动更新。项目做了两周, CLAUDE.md 可能还是最初的版本。 决策是断裂的 :AI 在某个会话中做了"选 PostgreSQL 而非 MySQL"的决策,但下一个会话看不到这个决策——除非你手动写进文档。 上下文是孤立的 :AI 知道当前要做什么,但不知道"为什么这样做"——上游做了什么决策、有哪些约束,这些信息丢失了。 文档会膨胀 :随着项目推进,记忆文件越来越长,信息密度越来越低,AI 和人都难以从中找到关键信息。 核心矛盾: 项目是一个持续演进的过程,但 AI 的记忆是一个需要手动维护的快照。 想法:用事件树做项目记忆 如果我们不用文档,而是用一棵 带依赖关系的事件树 来记录项目呢? 构建用户认证系统(项目目标) │ ├── 技术选型 ✅ done │ └── outcome: 选了 Rust + SQLite ,团队熟悉且单机部署足够 │ │ │ ├── 数据库设计 ✅ done │ │ └── outcome: 用户表 + 角色表 + 权限表,RBAC 模型 │ │ │ │ │ └── 实现 JWT 认证 ◐ doing ← 当前任务 │ │ │ └── API 网关设计 ○ todo │ └── 部署方案 ○ todo 每个事件有状态( todo / doing / done / abandoned ),事件之间有依赖关系。当一个事件完成时,记录它的 决策结果( outcome ) ——为什么这样做、选了什么、有什么约束。 关键机制: 当你查看某个事件时,系统沿依赖链向上遍历,把所有已完成事件的 outcome 自动组装成上下文。 AI 不需要翻文档、不需要人手动维护记忆。它只需要问"下一步做什么",就能拿到: 当前任务是什么 上游做了哪些决策、为什么 有哪些约束条件 这棵树能做什么 跨会话的决策连续性 AI 编程助手每次新会话都从零开始,但项目不是从零开始的。事件树的核心能力是 把上游的决策结果自动注入到当前任务的上下文中 。 当一个 AI 会话开始工作前,它问系统"下一步做什么",系统不仅告诉它当前该做哪个任务,还会沿着依赖链把前面所有已完成事件的决策结果组装在一起——选了什么技术、为什么这样设计、有哪些约束。AI 拿到的不是一个孤立的任务标题,而是站在一系列已验证的决策之上开始工作。当这个会话结束时,它把自己的决策结果写回事件树,下一个会话就能看到。决策链就这样跨会话地生长。 位置感知 在大型项目中,每个人(或每个 AI 会话)通常只负责一个局部。事件树让执行者看到自己在整体中的位置——项目的最终目标是什么,我当前的任务在哪个节点上,上游做了哪些决策把我带到了这里,下游还有哪些任务依赖我的产出。这不是简单的"待办清单",而是 带方向感的地图 。 计划的弹性 项目过程中计划会变。事件可以被废弃,废弃时记录原因。被废弃的事件不影响依赖链的推进(下游事件仍然可以继续),但原因会被保留在历史中。AI 知道"曾经考虑过这个方案但放弃了",不会在未来的会话中重复提议已经否决的方向。依赖关系本身也可以被调整——新增依赖、移除依赖、拆分事件——树的结构跟着项目的理解一起演化,而不是一开始就固定不变。它可能是一个完整的计划,执行一半中途修改计划,也可能只规划了大方向,细枝末节完全没有计划,但都适用。 对人也有用 不只是给 AI 用的工具。一个人的项目也可以用它来记录想法、理清顺序、记住当时的思考过程。三五天后回来, list 一看就知道有什么没做完, show 一下就能回想起当时为什么想做这件事。 畅想:自动执行循环 上面描述的是人手动推进事件树的流程——人(或 AI 会话)问"下一步做什么",拿到任务和上下文,执行完后手动写回结果。 但如果把这件事自动化呢? 设想这样一个循环:系统查看事件树,找到下一个可执行的任务,把任务描述和完整的决策链上下文打包,发送给执行器(一个 AI Agent ,比如 Claude Code 会话)。执行器拿到这些信息后开始工作——写代码、跑测试、解决问题。完成后,执行器把执行结果(做了什么决策、选了什么方案、遇到什么问题)反馈回来,系统自动更新事件树的状态和 outcome 。 然后系统再查看事件树。刚刚完成的任务可能解锁了新的可执行事件——原本因为依赖它而等待的任务现在就绪了。系统再次找到下一个任务,再次组装上下文,再次调用执行器。 事件树 ──查看下一步──→ 组装上下文 ──→ 调用执行器 │ 执行器完成任务 │ 事件树 ◀──更新状态── 接收反馈 ◀─────┘ │ └── 新任务就绪?──→ 是 ──→ 继续循环 否 ──→ 等待或通知人介入 在这个循环中,事件树扮演的不是"待办清单",而是 项目的状态机 。它知道哪些任务已经完成、哪些因为依赖而就绪、哪些因为上游未完成而被阻断。执行器不需要知道项目全貌,它只需要拿到当前任务的上下文(决策链),专注于执行。事件树负责记忆和调度。 更有意思的是,执行器的反馈不只是"完成了"。如果执行器发现当前任务的某个前提假设不成立,或者需要拆分成更细的子任务,或者发现一个更好的方向需要废弃原计划——这些信息反馈回事件树,树的结构就会相应调整。树在生长,不只是在推进。 人在这个循环中的角色从"逐步推进每个任务"变成"设定目标和方向,监控进展,在需要判断时介入"。日常的执行、记忆维护、上下文传递,全部自动化。 当然,这还很远。要让这个循环真正跑起来,需要解决很多问题:执行器如何可靠地反馈结构化信息、系统如何判断反馈质量、什么时候需要人介入、如何处理执行失败。但这棵树的结构——带依赖的事件、自动组装的决策链、状态的流转——天然地支持这个方向。 现在的实现 目前 vibecoding 了一版勉强可用的 knit ,核心是一个 Rust CLI 工具: 核心循环: knit next --json → 读取记忆(拿到下一步 + 决策链上下文) 执行任务 knit report done --outcome "..." → 写入记忆(记录决策) 循环 10 个命令覆盖了创建、查看、编辑、报告、推荐、废弃等操作。全部输出支持 --json ,AI 可以程序化消费。 存储是单文件 SQLite ,不需要服务器。LLM 推荐是可选的( knit next 可以配置 LLM 做智能排序,也可以不配置,用简单的依赖出度排序)。 事件树的 BFS 遍历自动沿依赖链收集决策链,钻石依赖会去重,被阻断的事件(上游还有未完成依赖)会标记 blocked 。废弃的事件被跳过,但原因保留。 SO ? 不知道大家怎么看这样的一个设计,我也不知道是否有一定的可行性,它看起来适合做很多方面的工作,也可以考虑它只为 agent 的项目记忆服务,目前的定位其实也有点模糊。 如果你有兴趣看看项目,里面的文档有更多信息 qkyufw/knit 欢迎评论
历史会话还能继续使用。 但是如果发起新的会话的话,就会一直转圈。 无法发起会话,而且页面卡死,只有切换左边的树才会恢复。 1 个帖子 - 1 位参与者 阅读完整话题
先上结论: 切一个新会话即可。 历程: 今天看DeepSeek又降低了缓存读取的价格,遂萌发出直接使用DeepSeek官key来写代码的想法,使用CPA配置了DeepSeek的供应商内容之后,无论是Claude供应商还是OpenAI供应商,都会出现类似下面内容的提示: The `content[].thinking` in the thinking mode must be passed back to the API 于是乎我在CPA的issue区搜索了 codex DeepSeek ,定位到这篇(无正确解答): github.com/router-for-me/CLIProxyAPI DeepSeek v4 系列模型在调用工具轮次中未正确回传`reasoning_content` 导致报错 已打开 04:42AM - 24 Apr 26 UTC 已关闭 03:44AM - 26 Apr 26 UTC GD-Slime **Is it a request payload issue?** - [x] Yes, this is a request payload issue. I … am using a client/cURL to send a request payload, but I received an unexpected error. - [ ] No, it's another issue. **If it's a request payload issue, you MUST know** Our team doesn't have any GODs or ORACLEs or MIND READERs. Please make sure to attach the request log or curl payload. **Describe the bug** 使用 OpenAI 兼容格式提供商接入DeepSeek官方API. 然而, DeepSeek v4 系列模型要求必须在调用工具的轮次后回传`reasoning_content`, 否则会400报错. 当前CPA版本未实现这一功能. 见 https://api-docs.deepseek.com/zh-cn/guides/thinking_mode **CLI Type** Codex CLI **Model Name** DeepSeek v4 Series **LLM Client** Codex CLI **Request Information** {"error":{"message":"The `reasoning_content` in the thinking mode must be passed back to the API.","type":"invalid_request_error","param":null,"code":"invalid_request_error"}} **Expected behavior** 正常调用工具 **Screenshots** <img width="566" height="193" alt="Image" src="https://github.com/user-attachments/assets/b7a2eb51-7300-4f1b-b3f5-8ea4a0984745" /> 然而老大只表示: 而真正解决的帖子是 makoMakoGo 在下面这个帖子里说的(节选): github.com/farion1231/cc-switch cc switch接deepseek V4报“API Error: 400 {"error":{"message":"The `content[].thinking` in the thinking mode must be passed back to the API.","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}” 已打开 05:10AM - 24 Apr 26 UTC cl851111 bug ### Self Checks / 自检 - [x] I have read the [FAQ](https://github.com/farion1231/ … cc-switch#faq) section in README. 我已阅读 README 中的[常见问题](https://github.com/farion1231/cc-switch#常见问题)。 - [x] I have searched for [existing issues](https://github.com/farion1231/cc-switch/issues), including closed ones. 我已搜索过[已有的 Issue](https://github.com/farion1231/cc-switch/issues),包括已关闭的。 ### CC Switch Version / 版本号 v3.14.1 ### Operating System / 操作系统 Windows ### Related App / 涉及应用 Claude Code ### Steps to Reproduce / 重现步骤 希望能接deepseek最新模型deepseek-v4-pro ### Expected Behavior / 期望行为 希望能接deepseek最新模型deepseek-v4-pro ### Actual Behavior / 实际行为 _No response_ ### Additional Context / 补充信息 _No response_ 你是不是在旧对话切换到ds了,请注意: DeepSeek V4 thinking mode 不兼容“从其他 provider 切过来的旧 Claude Code 会话历史”。 这一刻我恍然大悟!对哦…前天DeepSeek-V4刚出的时候,在Rikkahub中调用也是发现旧窗口会报错,而新建窗口却又马上好了! 在想到这一层时,我马上在codex中新建了一个窗口测试,你猜怎么着…成了 由此给各位佬友分享一下经验…希望大家能顺利用上DeepSeek-V4. 以上。 3 个帖子 - 2 位参与者 阅读完整话题