前段时间分享了 我的「CodeX / Claude Code + Hermes + 知识库」工作流,看到很多朋友比较感兴趣。
经过这么长时间的实践,确实发现使用这套工作流的效果不错!所以今天详细分享一下我的知识库怎么构建的,思路供大家参考哦 ~^-^
那么【第二弹】开始吧 ~
一、知识库架构图(AI总结我的知识库生成,去除了敏感信息):

一句话总结:
我的每个项目、服务、工具和运维流程都有自己的单页,AI 先读索引,再读对象页,不盲目全库搜索。注意:源码是最终事实源。
二、怎么生成自己的知识库
知识库主体参考的开源项目:github.com/silverbulletmd/silverbullet
知识库日志和沉淀参考的开源项目:github.com/luotwo/llm-wiki
可以直接发给 AI,让它给你生成知识库!
三、怎么使用知识库
你需要给 AI 设置全局提示词(只说思路,具体可以让 AI 结合自己的项目生成):
我不希望 AI 每次上来就把所有资料全读一遍,那样慢、浪费上下文,也容易被无关信息干扰。
所以一些限制是必须的:
- AI每次只看目录,再看单页。遇到具体项目、服务等问题时,由知识库入口 → 目标单页 → 具体问题/配置页。
- 知识库信息足够准确才直接回答。拿不准就去读源码。。
- 知识库只放长期复用的事实。AI 发现长期事实变更(接口、部署方式、踩坑经验等等),才更新知识库,一直变化的、临时的规则请写到 AGENT.md / CLAUDE.md 放每个项目的根目录。
- 知识库日志只记有价值的。不要改个变量、修个小 bug 也往日志记录,没有意义。
- 知识库禁止放密钥、密码等一切敏感信息。必须要放也只可以放引用路径,所谓敏感信息就是能不能公开发到 Linux DO 的都算 ^-^
三、多环境如何使用同一个知识库
每台设备需要初始化一次路径,也只需要一次
知识库正文里不直接写死 /opt/xxx、D:/xxx 这种真实路径,而是写成变量,比如:
repo: ${QCODE_CONSOLE_REPO}
config: ${QCODE_CONSOLE_CONFIG_DIR}/console.env
logs: ${VAR_LOG}/xxx.log
不同机器各自维护一份本机映射:env/local-paths.yaml,这个文件是 gitignored,不提交。
Linux、mac、Windows 可以 clone 同一份知识库,但每台机器的 ${VAR} 指向自己的真实路径。
AI 第一次访问知识库会自动执行知识库路径初始化的脚本。
这里贴一下大家参考,也可以让 AI 自己写一个
#第一次在新机器上用,自动生成本机路径映射:
cd /opt/knowledge-base
python3 scripts/resolve-paths.py --list
#scripts/scripts/resolve-paths.py
#!/usr/bin/env python3
"""Resolve ${VAR} paths using the machine-local mapping.
Default behavior is lazy and agent-friendly: if env/local-paths.yaml does not
exist, this script creates it once by scanning common local paths.
"""
from __future__ import annotations
import argparse
import os
import re
import socket
import subprocess
import sys
from pathlib import Path
ROOT = Path(os.environ.get("KB_ROOT", "/opt/knowledge-base"))
LOCAL_MAPPING = ROOT / "env" / "local-paths.yaml"
VAR_RE = re.compile(r"\$\{([A-Z0-9_]+)\}")
# Documentation/template placeholders, not real host mappings.
IGNORE_VARS = {"VAR", "PROJECT_REPO", "PROJECT_CONFIG_FILE"}
def parse_mapping(path: Path) -> dict[str, str]:
data: dict[str, str] = {}
if not path.exists():
raise FileNotFoundError(f"local path mapping not found: {path}")
in_paths = False
for raw in path.read_text(encoding="utf-8").splitlines():
line = raw.rstrip()
if not line.strip() or line.lstrip().startswith("#"):
continue
if re.match(r"^paths\s*:\s*$", line):
in_paths = True
continue
if in_paths:
if not line.startswith((" ", "\t")):
in_paths = False
continue
m = re.match(r"\s*([A-Z0-9_]+)\s*:\s*(.*?)\s*$", line)
if m:
val = m.group(2).strip().strip('"\'')
data[m.group(1)] = os.path.expanduser(val)
return data
def ensure_mapping(path: Path = LOCAL_MAPPING) -> bool:
"""Create local mapping once if missing. Returns True if created."""
if path.exists():
return False
bootstrap = ROOT / "scripts" / "kb-host-bootstrap.py"
if not bootstrap.exists():
raise FileNotFoundError(f"bootstrap script not found: {bootstrap}")
subprocess.run([sys.executable, str(bootstrap), "--write", "--output", str(path)], check=True)
return True
def mapping_path() -> Path:
return LOCAL_MAPPING
def host_name() -> str:
return os.environ.get("KB_HOST") or socket.gethostname()
def resolve(text: str, mapping: dict[str, str]) -> str:
def repl(m: re.Match[str]) -> str:
name = m.group(1)
if name not in mapping:
raise KeyError(name)
return mapping[name]
return VAR_RE.sub(repl, text)
def scan_vars() -> dict[str, set[str]]:
found: dict[str, set[str]] = {}
for path in ROOT.rglob("*.md"):
if ".git" in path.parts:
continue
rel = str(path.relative_to(ROOT))
for var in VAR_RE.findall(path.read_text(encoding="utf-8", errors="replace")):
if var in IGNORE_VARS:
continue
found.setdefault(var, set()).add(rel)
return found
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("value", nargs="?", help="string/path containing ${VAR} references")
ap.add_argument("--list", action="store_true", help="list local path mapping")
ap.add_argument("--check", action="store_true", help="check all ${VAR} references in markdown files are mapped")
ap.add_argument("--mapping", default=str(LOCAL_MAPPING), help="mapping file path; default env/local-paths.yaml")
ap.add_argument("--no-create", action="store_true", help="do not auto-create the local mapping if missing")
args = ap.parse_args()
mp = Path(args.mapping)
if not mp.is_absolute():
mp = ROOT / mp
created = False
try:
if not args.no_create:
created = ensure_mapping(mp)
mapping = parse_mapping(mp)
except FileNotFoundError as e:
print(f"ERROR: {e}", file=sys.stderr)
print("Hint: run `python3 scripts/kb-host-bootstrap.py --write` once, or omit --no-create.", file=sys.stderr)
return 2
except subprocess.CalledProcessError as e:
print(f"ERROR: failed to create local mapping: {e}", file=sys.stderr)
return 2
if created:
print(f"created local mapping: {mp}", file=sys.stderr)
if args.list:
print(f"host: {host_name()}")
print(f"mapping: {mp}")
for k in sorted(mapping):
print(f"{k}={mapping[k]}")
if args.check:
refs = scan_vars()
missing = sorted(v for v in refs if v not in mapping)
if missing:
print("Missing path mappings:")
for var in missing:
print(f"- {var}: {', '.join(sorted(refs[var]))}")
return 1
print(f"OK: all {len(refs)} referenced path variables are mapped in {mp}")
if args.value is not None:
try:
print(resolve(args.value, mapping))
except KeyError as e:
print(f"ERROR: unmapped variable: {e.args[0]}", file=sys.stderr)
return 1
if not (args.list or args.check or args.value is not None):
ap.print_help()
return 0
if __name__ == "__main__":
raise SystemExit(main())
本人迫不得已,要在Windows/Mac/Linux上工作,才想的这个办法,否则维护太困难!!
四、使用前的痛点和优缺点总结
痛点:
欸?我这个服务架构怎么实现的,端口号是多少?部署在docker吗,挂载到哪个目录了?
欸?我这个服务绑定的哪个域名?
欸?我这个服务里用到的第三方服务密钥配置在哪里了?
欸?之前看到一篇很好的文章 / 一个很优秀的项目,想不起来了!
…
使用知识库之后
优点:
项目职责和配置清晰明了。
运维历史可追溯。
个人知识沉淀了,走到哪里都能复用。
…
缺点:
Token 稍微多花费了一些,这个还在持续优化中,怎么让 AI 更有效率,更少检索来实现更好的效果。已经从每次读 1 万到 2 千左右了。
PS: 在一个上下文(Session)中,这些额外Token占用会带到每一轮对话中,看你是否能够承受!
只要用心维护知识库,你的知识永远会为你服务。