Skip to content

知识库与 Wiki 协编

本章目标:

  1. 理解 a-cdm 知识库为什么不自建存储,而是把 GitLab 单仓 a-cdm/llm-wiki(raw/ + wiki/ 两层 Karpathy 模式)当唯一真源,以及 acdm-backend 如何做一层薄代理
  2. 看懂 PUT /wiki/file 的乐观锁双层防御(预检 sha 比对 + GitLab last_commit_id 原子 CAS)如何把多人协编冲突收敛成业务码 40901
  3. 弄清 AI 改写/续写 SSE 端点、[human]/[ai-assist]/[kc] 服务端强制 commit 前缀、index.json 全局类目 CRUD 与前端 WikiEditor 协作组件的全链路

TL;DR

a-cdm 知识库没有自己的数据库表,真源是 GitLab 仓 a-cdm/llm-wiki,目录分 raw/(原始材料,compiler 写)和 wiki/(结构化知识,人 + AI 编辑)两层。wiki_router.py 通过 GitLabClient 代理 GitLab v4 REST 做五件事:列树/读文件/历史/写文件/改名删除。多人编辑同一文件靠乐观锁:前端打开时拿 last_commit.sha,保存时回传 expected_sha,服务端先预检比对当前 HEAD,再把它作为 last_commit_id 交给 GitLab 做原子 CAS,任一层不一致都翻译成 40901 冲突码,前端弹"已被他人修改"对话框。commit message 前缀([human]/[ai-assist])由服务端强制,不信任前端。AI 协编是两个 SSE 流式端点,走 ai_registrywiki_ai_rewrite/wiki_ai_extend role,只生成建议、用户点"采用"才回写编辑器、不自动 commit。全局知识库分类卡通过 knowledge_router 对仓根 index.json 做 CRUD,同样用 CAS。

Overview(为什么知识库要"寄生"在 GitLab 而不是自建)

知识库要回答一个问题:一个企业级 PMO 知识库,如何同时满足版本历史、多人协编冲突检测、AI 自动写入、署名审计、可被 Agent 检索这五件事,又不让 a-cdm 自己背一套内容存储 + 版本引擎的债?

直觉做法是建张 wiki_doc 表,字段塞 markdown,加个 version 列做乐观锁。但这条路有四个硬伤:

  1. 版本历史要重造轮子:diff、blame、回滚、按文件查 commit 历史,全得自己写,而 git 天生就有
  2. AI 写入与人写入混在一起无法审计:谁改的、人改还是 AI 改、是会议编译器灌进来的还是 BA 报告回写的,需要一个统一的"署名"机制
  3. Agent 检索要再建索引:deer-flow 的 lead_agent 要能搜知识库,自建存储还得再挂一套全文搜索
  4. 多写入源:不只是前端编辑器,还有 BA 报告回写([ai-assist])、会议/资料编译器([kc]/[material]),它们都要往同一个地方写

答案是把 GitLab 单仓 gitlab.r7.chinasoftinc.com/a-cdm/llm-wiki 当唯一真源,采用 Karpathy LLM-Wiki 模式的多类型扩展——raw/(原始材料层,编译器写)+ wiki/(结构化知识层,人 + AI + 编译器写)两层目录(openspec/specs/llm-wiki/spec.md:37-60)。git 自带版本历史/diff/blame;commit message 前缀解决署名审计;GitLab 的 last_commit_id 参数天生是个原子 CAS,解决多人冲突;GitLab Search API 让 Agent 经 MCP 直接检索(见第 26 章)。acdm-backend 只做一层薄代理:把前端友好的 REST 形状翻译成 GitLab v4 调用,加一层路径校验和前缀强制。

Architecture:薄代理 + 单一 GitLab 客户端

整个知识库子系统的代码量很小,因为重活全交给 GitLab。三个 router 都在 acdm-backend/app/main.py:456-463 挂载,共享同一个 GitLabClient,凭据从 settings 三个字段读(config.py:104-114)。

组件职责入口文件Source
wiki_routertree/file/history/PUT/DELETE/rename/meta/ai 端点(挂 /wiki 前缀)app/api/wiki_router.pyacdm-backend/app/api/wiki_router.py:48
knowledge_router全局类目卡 CRUD(端点自带 /knowledge 前缀)app/api/knowledge_router.pyacdm-backend/app/api/knowledge_router.py:28
GitLabClientGitLab v4 REST 封装(列树/读文件/commits/commit/search)app/util/gitlab_client.pyacdm-backend/app/util/gitlab_client.py:30
KnowledgeServiceindex.json 读写 + 类目文档数计算(CAS)app/services/knowledge_service.pyacdm-backend/app/services/knowledge_service.py:28
LLMClientAI 改写/续写的 SSE 流式 chat(按 role 选 provider)app/util/llm_client.pyacdm-backend/app/api/wiki_router.py:726
WikiSyncServiceBA 报告 P1-P5 内容回写 wiki([ai-assist])app/services/ba_report/wiki.pyacdm-backend/app/services/ba_report/wiki.py:24
KnowledgeWorkspace前端中央工作区容器,Viewer/Editor/History 切换components/.../wiki/KnowledgeWorkspace.tsxdeer-flow/frontend/src/components/workspace/acdm/wiki/KnowledgeWorkspace.tsx:63
WikiEditorMonaco 编辑器 + 乐观锁保存 + AI 面板宿主components/.../wiki/WikiEditor.tsxdeer-flow/frontend/src/components/workspace/acdm/wiki/WikiEditor.tsx:43

GitLabClient.__init__ 是 fail-fast 的:GITLAB_WIKI_PATGITLAB_WIKI_PROJECT_ID 没配就抛 RuntimeError,由 router 的 _get_client() 转成业务码 50301(wiki_router.py:60-65)。Project ID 支持三种形式——numeric 123 / slug a-cdm/llm-wiki(构造时自动 URL-encode)/ 已编码 a-cdm%2Fllm-wiki(gitlab_client.py:37-42),这让同一份代码既能跑在 numeric-id 的 GitLab 也能跑在 slug 寻址的环境。

Components / Subsystems

GitLabClient — 唯一的 GitLab 出口

职责:把 GitLab v4 REST 封装成 5 类异步方法,所有写入源(router / KnowledgeService / WikiSyncService / MCP wiki tool)共用它。

关键类:GitLabClientacdm-backend/app/util/gitlab_client.py:30

关键方法:

  • list_tree(path, ref, recursive)(gitlab_client.py:47):GET /repository/tree,自动翻页(读 x-next-page header 循环直到取完),recursive=true 时递归。中文/特殊字符路径直接传原始串,httpx 对 params 自动 URL-encode。
  • get_file_raw(path, ref, timeout)(gitlab_client.py:86):GET /repository/files/:path/raw,path 用 quote(path, safe="") 编码;timeout 可被调用方覆盖(/wiki/meta 用 5s 短超时防慢文件拖垮批量)。
  • get_file_commits(path, per_page, ref)(gitlab_client.py:108):取单文件最近 N 条 commit,乐观锁的 sha 来源就是 commits[0]["id"]
  • commit_file(...)(gitlab_client.py:196):POST /repository/commits 单文件 create/update/delete,核心参数 last_commit_id 即 GitLab 原子 CAS——传了它且当前 HEAD 上该文件 last commit id 不等于此值,GitLab 返回 400 带 "changed since" / "last_commit_id"
  • commit_actions(actions, ...)(gitlab_client.py:150):通用多动作 commit,delete/move/批量都走它。
  • search_wiki_blobs(query, limit, path_prefix)(gitlab_client.py:121):GET /search?scope=blobs 全仓内容搜索,path_prefix客户端侧过滤(GitLab API 本身不支持 path 参数),供 knowledge-sources MCP 用(见第 26 章)。

实现要点:web_url_base(gitlab_client.py:179)在 PROJECT_ID 为 numeric 时返回空串——拼不出可读 UI URL,这种情况让前端不显示 commit 链接,而不是给个坏链接。

wiki_router — 路径校验是第一道闸

职责:把 GitLab tree/file/commits 响应投影成 ApiResponse 形状,并在写入前强制路径与前缀规则。

关键方法:三层路径校验体现了"读宽写严":

  • _validate_read_path(wiki_router.py:68):GET 用,只拒路径穿越(.. / 以 / 开头)
  • _validate_write_path(wiki_router.py:78):PUT 用,要求落在 wiki/raw/ 前缀下且以 .md 结尾(Phase 1 仅允许 markdown,防 PMO 写到仓根搞乱)
  • _validate_wiki_only_path(wiki_router.py:91):DELETE / rename 专用,更严——只允许 wiki/ 前缀,明确不允许删 raw/ 或仓根(spec 强约束)

前缀提取:_extract_prefix(wiki_router.py:54)用正则 ^\[([a-zA-Z][a-zA-Z-]*)\] 从 commit message 提取首个 [xxx],投到 WikiCommitInfo.prefix,前端据此显示 human/ai-assist/kc/material/bot 的 badge。

DELETE 与 rename 都用 require_admin 依赖(wiki_router.py:443,498)——破坏性操作只有管理员能做,普通成员只能 PUT(创建/更新)。rename 当前期只支持同目录原子改名(_parent_dir 相等),跨目录走 50304 让用户去 GitLab UI 操作(wiki_router.py:510-514)。

KnowledgeService — 全局类目卡的 index.json

职责:维护仓根 index.json(全局知识库的分类卡元信息),并实时算每个类目下的 .md 文档数。

关键方法:

  • get_index(ref)(knowledge_service.py:32):读 index.jsonjson.loadsIndexJson.model_validate;404 视为空 categories(返回 (空 IndexJson, ""));sha 单独用 get_file_commits 取。格式/结构错抛 50101
  • put_index(...)(knowledge_service.py:64):last_commit_id="" 走 create(GitLab 对 create 忽略此参),否则 update + CAS;GitLab 400 含 exist/changed/stale 翻译成 40901 冲突。
  • count_docs(path)(knowledge_service.py:107):两段策略——先非递归取当前层数 .md(快);当前层无 .md 但有子目录才递归兜底;超大目录(如 allmeta-manual 25585 节点)递归超时返回 (None, 错误) 而不是显示 0,这样前端能 tooltip 提示而非误导用户"这个类目没文档"。
  • count_docs_many(paths)(knowledge_service.py:175):asyncio.gather 并发数多个类目。

KnowledgeCategory(schemas/knowledge.py:13)有强约束:slug 必须匹配 ^[a-z][a-z0-9-]{1,30}$color 是 6 个枚举值之一、tags 最多 5 个。

前端 KnowledgeWorkspace / WikiEditor / WikiAIPanel

职责:KnowledgeWorkspace 是项目级 KB 和全局 KB 双头复用的中央工作区容器(KnowledgeWorkspace.tsx:63)——项目级传 rootPath="wiki/by-project/${id}" + canEdit=true,全局 KB 传 rootPath={category.path} + canEdit={user.is_admin}。注意 canEdit 只控按钮显隐,真鉴权闸门是后端 PUT /wiki/file(KnowledgeWorkspace.tsx:46),前端绕开按钮也写不进去。

WikiEditor(WikiEditor.tsx:43)用动态导入的 Monaco(ssr:false),baseSha prop 即打开时的 last_commit.sha,是乐观锁的客户端起点。EditorBridge(WikiEditor.tsx:119-158)是 editor 与 AI 面板之间的桥:getSelection/replaceSelection/appendToEnd/getFullContent,任何 AI 回写都置 hasAIEdit=true,保存时 source 自动变 "ai-assist"(WikiEditor.tsx:82)。

WikiAIPanel(WikiAIPanel.tsx:33)两种模式:rewrite 改写选区(无选区则整文)、extend 续写到文末。SSE 流式预览在面板内,用户点"采用"才经 bridge 回写 Monaco,不自动 commit——这是有意的:AI 产出要人确认,符合 spec "前端在 diff 预览 → 用户点应用后插入编辑器,不自动 commit"(openspec/specs/llm-wiki/spec.md:256)。

Data Flow:乐观锁保存的双层防御

这是本章最关键的算法。多人编辑同一 wiki 文件,如何在不引入分布式锁的前提下,既不丢别人的改动,又给用户清晰的错误?

PUT /wiki/file(wiki_router.py:283)的设计是预检 + GitLab CAS 双层防御,把"读到写之间被别人改了"这个 race 收敛成业务码 40901(WIKI_CONFLICT_CODE,wiki_router.py:264)。

逐步拆解:

  1. 路径与 message 校验(wiki_router.py:304-307):写路径必须 wiki//raw/ 前缀 + .md,message 非空。
  2. 预检层(wiki_router.py:317-346):只有传了 expected_sha 才走。get_file_commits(per_page=1) 取当前 HEAD 上该文件的 last commit id,与客户端的 expected_sha 比对。不一致直接 40901,错误信息带 current_sha=X, expected_sha=Y 让前端能拉 diff。文件不存在(404)但用户传了 expected_sha,说明他以为文件还在 → 也判冲突(被删了)。
  3. GitLab CAS 层(wiki_router.py:348-358):_do_commitexpected_sha 作为 last_commit_id 传给 GitLab。为什么预检过了还要这一层? 代码注释明确写了"预检→提交间有 race"(wiki_router.py:296):预检到提交之间存在窗口,另一并发提交在此窗口内落地后预检的结论就失效了。GitLab 的 last_commit_id 是服务端原子 CAS,把这个窗口堵死——这是预检层无法覆盖的部分。
  4. 冲突翻译(wiki_router.py:362-398):GitLab 返 400 分三种——"doesn't exist"(文件不存在:有 expected_sha → 被删冲突 40901;无 → fallback create);_looks_like_cas_conflict 命中 last_commit_id/changed since/conflict 等关键词(wiki_router.py:269-280)且有 expected_sha → race 冲突 40901;其它 → 50305 运维错。

前端对 40901 的处理(WikiEditor.tsx:88-100):doSave 捕获 WikiError.code === WIKI_CONFLICT_CODE 时弹 window.confirm,选"确定"= doSave(false) 这次不带 sha 强制覆盖;选"取消"= 放弃,提示退出重开合并。withLock 参数(WikiEditor.tsx:74)即是否带 baseSha,这就是"强制覆盖"的实现:退化回旧的"后写覆盖前"行为。

Data Flow:AI 协编 SSE 流

AI 改写/续写不走任何写入,纯生成。POST /wiki/ai/rewrite(wiki_router.py:744)和 /wiki/ai/extend(wiki_router.py:761)都返回 EventSourceResponse,经 _stream_llm 包装 LLMClient.chat_stream

后端 _stream_llm(wiki_router.py:712)的协议:LLMClient(role_id) 构造失败(ai_registry 抛 50401-50404RuntimeError)→ yield event=error;正常时逐 chunk yield event=delta,结束 yield event=done;HTTP 异常 yield event=errorrole_idwiki_ai_rewrite / wiki_ai_extend,在 ai_roles.yaml:63-72 注册——provider jointpilot、model kimi-k2.6,且 force_overrides.thinking 关掉(ai_roles.yaml:33-39,工具型短任务 thinking 没价值还会挤光 content)。

_AI_SYSTEM_PROMPT(wiki_router.py:688)四条硬约束:只输出结果文本、不加解释/前言、不要代码块围栏包裹、保持中英文与 markdown 语法。/wiki/ai/extend 还会保守截断光标前文本到后 2000 字符防上下文爆(wiki_router.py:765)。

前端 parseSseStream(wiki-api.ts:164)是个 AsyncGenerator,逐行解析 data: 前缀、跳过 [DONE],WikiAIPanelfor await 消费并累积到 preview,AbortController 支持中途"停止"(WikiAIPanel.tsx:63-67)。

速查表:Wiki / Knowledge API 端点

端点方法鉴权作用Source
/wiki/treeGET登录列单层目录(懒加载)acdm-backend/app/api/wiki_router.py:111
/wiki/validate-pathGET登录探测路径在仓中是否可达(项目设置弹窗用)acdm-backend/app/api/wiki_router.py:131
/wiki/listGET登录递归列所有 .md(前端搜索建索引)acdm-backend/app/api/wiki_router.py:189
/wiki/fileGET登录读单文件原文 + 最近 commitacdm-backend/app/api/wiki_router.py:210
/wiki/historyGET登录文件最近 N 条 commit 历史acdm-backend/app/api/wiki_router.py:240
/wiki/filePUT登录创建/更新(乐观锁 + [source] 前缀)acdm-backend/app/api/wiki_router.py:283
/wiki/fileDELETEadmin删除单文件([human] 前缀)acdm-backend/app/api/wiki_router.py:440
/wiki/file/renamePUTadmin同目录原子改名(GitLab move)acdm-backend/app/api/wiki_router.py:495
/wiki/metaGET登录批量取 frontmatter/H1/首段(≤50 路径)acdm-backend/app/api/wiki_router.py:660
/wiki/ai/rewritePOST登录选区改写 SSE 流acdm-backend/app/api/wiki_router.py:744
/wiki/ai/extendPOST登录文末续写 SSE 流acdm-backend/app/api/wiki_router.py:761
/knowledge/categoriesGET登录读 index.json + 实时算 doc_countacdm-backend/app/api/knowledge_router.py:39
/knowledge/categoriesPOSTadmin新增类目(CAS)acdm-backend/app/api/knowledge_router.py:61
/knowledge/categories/{slug}PUTadmin改类目(CAS,slug 不可改)acdm-backend/app/api/knowledge_router.py:86
/knowledge/categories/{slug}DELETEadmin删类目(CAS)acdm-backend/app/api/knowledge_router.py:122

速查表:commit message 前缀(署名审计)

前缀写入源谁写Source
[human]PUT(source=human)/ DELETE / rename人工编辑acdm-backend/app/api/wiki_router.py:311,469,540
[ai-assist]PUT(source=ai-assist)/ BA 报告回写编辑器内 AI 协编 / BA 报告 P1-P5 回写acdm-backend/app/services/ba_report/wiki.py:114
[kc]knowledge-compiler会议 → raw/meetings/openspec/specs/llm-wiki/spec.md:9
[material]material-compiler资料/模板 → raw/materials/openspec/specs/llm-wiki/spec.md:9

关键不变量:前缀服务端强制,不信任前端。put_filefull_message = f"[{req.source}] {req.message}"(wiki_router.py:311),即便前端 message 里已含 [xxx] 也再包一层,req.source 被 schema 限定为 Literal["human","ai-assist"](schemas/wiki.py:73)。这就是审计可信的根:谁、人/AI、何来源,全在 git log 里且无法伪造。

Implementation Details:frontmatter 解析的降级链

/wiki/meta 给类目卡片视图批量取标题/描述/标签,_parse_md_meta(wiki_router.py:583)有三级降级,且只读首 100 行防大文件 OOM:

python
# 摘自 acdm-backend/app/api/wiki_router.py:589-630(节选)
head = "\n".join(content.splitlines()[:100]) + "\n"
# 1) YAML frontmatter(--- ... --- 块,yaml.safe_load 取 title/description/tags)
m = _FRONTMATTER_RE.match(head)
# 2) fallback: 没 title → 找首个 H1(# 标题)
if title is None:
    h1 = _H1_RE.search(body)
# 3) fallback: 没 description → 找首个非空非标题段落首 80 字符

_fetch_meta_one(wiki_router.py:633)对单文件用 5s 短超时,失败填 error 字段不阻塞其它;get_metaasyncio.gather 并发(wiki_router.py:680),paths > 50 直接 50301。这套设计让一个慢文件或坏文件不会拖垮整个类目卡片渲染。

扩展指南:新增一个全局知识类目 / 新知识类型

加一张全局类目卡(经 API,需 admin):

jsonc
// POST /knowledge/categories  body
{
  "category": {
    "slug": "hr-policy",                 // ^[a-z][a-z0-9-]{1,30}$
    "path": "wiki/产品知识/HR",          // GitLab 仓内相对路径
    "title": "HR 制度",
    "description": "公司人力资源制度文档",
    "icon": "users",
    "color": "blue",                     // blue|orange|purple|emerald|red|slate
    "tags": ["制度", "HR"]               // ≤ 5
  },
  "expected_sha": "<GET 时拿到的 index.json sha>",  // "" = 创建第一份
  "message": "新增 HR 制度类目"
}

约束(从代码校验读出):

  • slug 不可与现有重复(knowledge_router.py:6940001),PUT 时 slug 不可改(路径参数与 body 不一致 → 40001,knowledge_router.py:94)。
  • expected_sha 必须等于当前 index.json sha,否则 40901(knowledge_router.py:72-73,100-101),这是和 wiki 文件同源的乐观锁。
  • 类目卡只是元信息;实际文档放在 category.path 指向的 GitLab 目录下,doc_count 运行时实时算。

加一种新原始知识类型(不动架构):按 spec(openspec/specs/llm-wiki/spec.md:71-74),在 raw/ 下新增子目录(如 raw/trainings/),wiki/ 按需加 by-domain 分类,不需要改 Architecture 或 API——这正是两层 Karpathy 模式的扩展性所在:raw/ 子目录对应资料来源类型,wiki/ 子目录对应检索维度。

程序化回写 wiki(如自定义报告):参照 WikiSyncService.sync_to_wiki(ba_report/wiki.py:30)——直接构造 GitLabClient,用 [ai-assist] 前缀 commit_file;注意它做章节级幂等(## 章节名 已存在则跳过,ba_report/wiki.py:97-100),且只回写 P1-P5 来源、P6 LLM 推断不写入 wiki(ba_report/wiki.py:62-65)。

Configuration

Config默认值含义Source
GITLAB_WIKI_BASE_URLhttps://gitlab.r7.chinasoftinc.comGitLab 实例地址acdm-backend/app/config.py:104
GITLAB_WIKI_PROJECT_ID""numeric / slug / URL-encoded 三形式acdm-backend/app/config.py:109
GITLAB_WIKI_PAT""(敏感,需 api+write_repository scope)Personal Access Token,不进库acdm-backend/app/config.py:113
WIKI_CONFLICT_CODE40901乐观锁冲突业务码acdm-backend/app/api/wiki_router.py:264
WIKI_META_MAX_PATHS50/wiki/meta 单次最多路径数acdm-backend/app/api/wiki_router.py:578
STALE_FILE / STALE_TREE60s / 5min前端 React Query 缓存 staleTimedeer-flow/frontend/src/core/acdm/wiki-hooks.ts:16-17
wiki_ai_rewrite / wiki_ai_extend rolejointpilot / kimi-k2.6AI 协编 LLM provider/modelacdm-backend/app/ai_roles.yaml:63-72

GITLAB_WIKI_PAT安全相关配置:scope 需含 api + write_repository,绝不进代码库(项目红线),dev 在 acdm-backend/.env、生产 /data/apps/*/.env 手 scp。PAT 失效时 validate-path 等返 50305 运维异常而非误判用户输入错。

Common Pitfalls / 实战 Tips

  • /wiki/list 对超大目录会超时:count_docs 注释明确点名 allmeta-manual 25585 节点递归会超时(knowledge_service.py:119),设计上超时返回 None 而非 0,前端要把 doc_count_error 渲染成 tooltip,别把 None 当 0 显示。
  • 不传 expected_sha = 无冲突检测:新建文件场景本就不该传(无历史 sha);但老调用方/前端漏传会退化成"后写覆盖前",静默丢改动。WikiEditor 的 baseSha prop 没传时就是这个旧行为(WikiEditor.tsx:33-38)。
  • rename 不支持跨目录:本期 _parent_dir 不等就 50304,要去 GitLab UI 手动操作(wiki_router.py:510-514)。
  • AI 协编不自动 commit:WikiAIPanel 点"采用"只回写 Monaco 内存,真正落库还要在 WikiEditor 填提交说明点保存(走乐观锁)。两步是有意设计,别期望 AI 直接改 GitLab。
  • get_file 的 commit 元信息是非阻断的:GitLab commits 查询失败时 content 仍返回、last_commit=null(wiki_router.py:228-237),此时前端拿不到 baseSha,乐观锁会退化——内容能渲染但保存无冲突保护。
  • 路径含中文不要预编码:list_tree 直接传原始中文串,httpx 自动 encode;手动 quote 反而双重编码导致 404(gitlab_client.py:55-58 注释)。

References

  • acdm-backend/app/api/wiki_router.py:1-777 — wiki 全部端点(本章主源:路径校验/乐观锁/AI SSE/meta 解析)
  • acdm-backend/app/util/gitlab_client.py:30-235 — GitLab v4 REST 封装(列树/读/commits/commit/search)
  • acdm-backend/app/api/knowledge_router.py:28-150 — 全局类目卡 CRUD(CAS)
  • acdm-backend/app/services/knowledge_service.py:28-179 — index.json 读写 + count_docs 两段策略
  • acdm-backend/app/schemas/wiki.py:1-155 — Wiki 请求/响应 schema(WikiFilePutRequest 的 expected_sha 语义)
  • acdm-backend/app/schemas/knowledge.py:1-71 — KnowledgeCategory / IndexJson 约束
  • acdm-backend/app/services/ba_report/wiki.py:24-148 — BA 报告 P1-P5 回写 wiki([ai-assist] + 章节幂等)
  • acdm-backend/app/ai_roles.yaml:33-72 — wiki_ai_rewrite/extend role(jointpilot kimi-k2.6,关 thinking)
  • deer-flow/frontend/src/components/workspace/acdm/wiki/WikiEditor.tsx:43-247 — Monaco + 乐观锁保存 + 冲突对话框
  • deer-flow/frontend/src/components/workspace/acdm/wiki/WikiAIPanel.tsx:33-203 — AI 改写/续写 SSE 面板
  • deer-flow/frontend/src/core/acdm/wiki-api.ts:78-184 — wiki API client + SSE 解析
  • deer-flow/frontend/src/core/acdm/wiki-hooks.ts:55-130 — React Query 读写 hooks + 缓存失效
  • openspec/specs/llm-wiki/spec.md:37-74 — raw/+wiki/ 两层 Karpathy 模式仓结构 + 扩展原则
PageRelationship
BA 专家报告工作流该章 8 步骤的 P1-P5 内容经本章 WikiSyncService 回写 wiki([ai-assist])
知识源 MCP 与 AgentToolsBFF本章 GitLabClient.search_wiki_blobs 是该章 knowledge-sources MCP 的检索底座
AI 角色注册表与模型矩阵本章 AI 协编经该章 ai_registry 的 wiki_ai_rewrite/extend role 选 provider
acdm-backend 控制面架构本章三个 router 在该章统一挂载,共享 auth 依赖与 ApiResponse 形状
项目管理与资源授权项目级 KB 的 rootPath 来自该章 project.gitlab_wiki_path 字段
环境变量凭据与降级开关本章 GITLAB_WIKI_PAT 等凭据在该章统一说明

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析