FriendForge 是一个把聊天记录制作成可复用 AI 朋友分身的开源工作台。它不训练新模型,也不微调 LLM 权重,而是把一段关系里的聊天语气、回应习惯、常用表达和少量上下文整理成独立的 Persona Bundle,再让兼容的大模型按这个 Bundle 的约束进行回复。
这个项目的初衷很简单:我试过一些同类项目,效果并不稳定。有的只是把聊天记录粗暴塞进 prompt,有的没有关系维度,有的没有验收流程,也很难把结果分发给其他程序使用。FriendForge 想把这件事做得更工程化:从聊天记录导入、脱敏、风格蒸馏、示例检索、验收、试聊,到导出给不同宿主使用,都放进一套可复用的工具链里。
公开仓库只包含代码、配置模板和一个合成示例 Bundle:Alex__to__Sam。真实聊天记录、真实朋友分身、本地产物、私有配置和 API key 都不会提交。
- V3 P1 运行时底座已经完成本地验证;P2 已实现显式 Schema 升级、不可变快照、状态事件、根初始化、手动快照、Pin/Release、Restore Preview 和同分支补偿恢复。V3 仍处于开发阶段,未迁移真实 Bundle,也未接入现有 Web、CLI 或 Adapter 用户入口。
- 将 Bundle 门禁决策集中到 Core,Web、CLI 和宿主适配器统一使用同一套规则。
- 修正
passed与approved的语义:已通过验收的 Bundle 保持passed,旧审批记录不会覆盖它;只有验收未通过但被显式放行时才显示approved。 - 对缺失、损坏、非 UTF-8 或结构不一致的验收报告与审批记录采用安全拒绝,避免错误放行和接口 500。
- 恢复
Host → Adapter → Core → Bundle单向分层,Web 与 CLI 不再直接加载 Persona 执行注入。 - 后端自动化测试增至 38 项,并改用现代 ASGI 测试客户端,消除旧 TestClient 兼容层的弃用警告。
FriendForge 会把聊天记录转换成一个个独立的 Persona Bundle:
owner:要被 AI 扮演的人。audience:这个分身正在对谁说话。owner→audience:某个人在某段关系里的特定说话方式。
例如:
Alex__to__Sam:扮演 Alex 对 Sam 说话。Sam__to__Alex:扮演 Sam 对 Alex 说话。
这两个方向不是同一个东西。所谓“反向蒸馏”不是把对话倒过来,而是从同一份聊天记录里抽取另一方的发言、风格、示例和近期上下文,重新制作一个独立 Bundle。
- 不微调模型:不训练、不微调 LLM 权重,降低成本和部署复杂度。
- 关系级人格包:每个
(owner→audience)都是独立目录,包含画像、风格、示例、近期上下文、验收报告等。 - Web 工作台:用 Vue 3 + Vite 提供可视化界面,用于浏览 Bundle、创建任务、查看进度、试聊天和导出产物。
- FastAPI 后端:提供 Bundle 浏览、制作任务、验收任务、配置、试聊、产物导出等接口。
- 流式任务进度:制作和验收任务通过 WebSocket 推送进度。
- 流式试聊:试聊窗口用 SSE 逐步输出模型回复。
- BYOK 大模型接入:支持 OpenAI-compatible API、Ollama、本地模型或自定义 base URL、model、api key。
- 导出门禁:未通过验收的 Bundle 默认不能导出
prompt、skill或card。 - 宿主适配器:可导出给 Agent、聊天程序、SillyTavern/角色卡等宿主使用。
- 隐私默认保护:真实聊天 CSV、真实 Bundle、导出产物、本地密钥默认被
.gitignore排除。
很多同类项目只停留在“把聊天记录塞进 prompt”这一步。FriendForge 更强调完整闭环:
- 把人格材料拆成可检查的 Bundle 文件,而不是一个不可维护的大 prompt。
- 用验收报告和门禁决定是否允许导出,而不是默认放行。
- 区分不同关系中的语气,避免“一个人对所有人都一个口吻”。
- 支持正向和反向两个独立 Bundle。
- Web 工作台和 CLI 同时可用,方便调试、演示和自动化。
- 不绑定单一模型,运行时可以切换 LLM provider。
- 真实数据默认不入库,更适合在本地制作私人分身。
FriendForge 保持单向分层:
Host
-> Adapter
-> Core
-> Persona Bundle
含义:
Host:真正使用分身的宿主,比如 Agent、聊天程序、角色卡程序。Adapter:产物分发层,负责导出prompt、skill、card。Core:核心逻辑层,负责制作、验收、检索、注入块渲染和增量更新。Persona Bundle:每个朋友分身的独立资料包。
核心目录:
digital-twin/core/build.py:从聊天 CSV 制作 Persona Bundle。verify.py:执行 6 维验收并生成report.json。retriever.py:根据输入消息检索相关 few-shot 示例。injector.py:唯一对外注入接口,渲染纯文本 prompt block。persona.py:加载 Bundle。update.py:增量更新 Bundle。
digital-twin/adapters/emit_prompt.py:生成运行时注入块。emit_skill.py:导出 Agent skill。emit_card.py:导出角色卡。
digital-twin/web/backend/- FastAPI 应用、任务管理、WebSocket、SSE、上传、配置和产物接口。
digital-twin/web/frontend/- Vue 3 + Vite + Pinia 工作台。
digital-twin/config/- 默认配置、schema 元数据和运行时配置加载。
digital-twin/personas/- 公开仓库默认只保留合成示例
Alex__to__Sam。
- 公开仓库默认只保留合成示例
FriendForge/
README.md
digital-twin/
cli/ # CLI 入口:python -m cli.dtwin
config/ # 默认配置、schema 和加载器
core/ # 制作、验收、检索、注入、更新
llm/ # BYOK 大模型客户端和预设
adapters/ # prompt / skill / card 导出
personas/ # 公开仓库只放合成示例
tests/ # 后端和 API 测试
web/
backend/ # FastAPI 后端
frontend/ # Vue 3 工作台
推荐环境:
- Windows 11
- Python 3.13.12(
digital-twin/.python-version) - uv 0.11.16(由
pyproject.toml强制匹配) - Node.js 20.19.4(前端
.nvmrc) - npm 10.8.2(前端
package.json)
可选依赖:
sentence-transformers 5.6.0 + torch 2.12.1+cpu:完整档用于本地 embedding 和较大样本检索。- BYOK LLM provider:例如 OpenAI-compatible API 或本地 Ollama endpoint。
克隆仓库:
git clone https://github.com/1while1/FriendForge.git
cd FriendForge首次安装标准开发环境:
.\scripts\setup_environment.ps1 -Profile standard需要本地 Embedding 时改用 -Profile full。脚本会按 uv.lock 和 package-lock.json 重建依赖,并验证 Python、uv、Node、npm 与 lockfile 一致;不需要预先准备 .venv 或 node_modules。
启动后端:
cd digital-twin
uv run --locked --no-sync python -m uvicorn web.backend.app:app --host 127.0.0.1 --port 8000 --log-level warning启动前端:
cd digital-twin/web/frontend
npm run dev打开:
http://localhost:5173
健康检查:
http://127.0.0.1:8000/health
http://127.0.0.1:8000/api/stats
公开仓库自带 Alex__to__Sam 合成示例,所以启动后可以直接看到一个示例 Bundle。
Web 工作台主要页面:
- Dashboard:查看 Bundle 总数、门禁状态、策略分布和数据规模。
- Bundles:浏览所有 Persona Bundle。
- Bundle Detail:查看画像、风格、示例、验收报告、试聊和产物导出。
- Make:上传 CSV,创建新的 Bundle 制作任务。
- Jobs:查看制作、验收、更新任务的状态和流式日志。
- Config:编辑 LLM、检索、Web 上传等配置。
典型流程:
- 准备聊天 CSV。
- 在 Make 页面填写
owner和audience。 - 启动制作任务。
- 等待 WebSocket 进度完成。
- 进入 Bundle Detail 查看结果。
- 运行 Verify 或重新验收。
- 在试聊窗口测试语气。
- 验收通过后导出
prompt、skill或card。
所有 CLI 命令建议在 digital-twin/ 目录下运行:
cd digital-twin制作 Bundle:
uv run --locked --no-sync python -m cli.dtwin make <chat.csv> --owner "Alex" --audience "Sam"验收 Bundle:
uv run --locked --no-sync python -m cli.dtwin verify Alex__to__Sam生成 prompt 注入块:
uv run --locked --no-sync python -m cli.dtwin emit prompt Alex__to__Sam --query "今天有点累"导出 Agent skill:
uv run --locked --no-sync python -m cli.dtwin emit skill Alex__to__Sam导出角色卡:
uv run --locked --no-sync python -m cli.dtwin emit card Alex__to__Sam增量更新:
uv run --locked --no-sync python -m cli.dtwin update Alex__to__Sam --append-csv <new.csv> --only recent通过 CLI 启动 Web 后端:
uv run --locked --no-sync python -m cli.dtwin web --reload生成后的 Bundle 通常长这样:
personas/Alex__to__Sam/
persona.json
style.md
recent_context.md
examples.jsonl
exemplars.jsonl
report.json
approval.json # 可选,手动 approve 留痕
embeddings/ # 可选,默认不入库
主要文件:
persona.json:Bundle 元数据和检索策略。style.md:从聊天记录蒸馏出的风格指纹。recent_context.md:近期上下文摘要。examples.jsonl:few-shot 示例。exemplars.jsonl:用于静态导出的代表性示例。report.json:验收结果和门禁状态。approval.json:手动放行记录。
FriendForge 在导出产物前会检查门禁状态:
passed:验收通过,可以导出。approved:验收未完全通过,但有手动 approve 记录。refused:验收失败,且没有手动 approve。pending:还没有验收报告。
这样可以避免把质量不足或未经检查的分身直接导出给宿主使用。
公开仓库按默认规则避免泄露个人数据:
- 原始聊天 CSV 不入库。
digital-twin/personas/*默认不入库,只保留合成示例。digital-twin/dist/不入库。digital-twin/config/config.toml不入库。.env和*.env不入库。- API key 应通过本地配置或环境变量提供,不要提交到 Git。
如果你要发布自己的 fork,建议先做一次 secret/PII 扫描,确认只有合成示例数据被 Git 跟踪。
感谢 WeFlow 这个开源项目。它帮助我非常方便地导出聊天记录,让 FriendForge 的数据准备流程顺畅了很多。
后端测试:
cd digital-twin
uv run --locked --no-sync python -m unittest discover -s tests -v前端构建:
cd digital-twin/web/frontend
npm ci
npm run build环境契约检查:
digital-twin\.venv\Scripts\python.exe scripts\check_environment.py --profile standard空白字符检查:
git diff --checkMIT