Friend 开源项目实战:用 GPT-4o 构建 Smart Facts Collector,将非结构化笔记智能提取并写入 OMI 记忆 API
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
本文以 Friend 仓库中 plugins/import/manual-import 的 Smart Facts Collector 为对象,讲解如何用 Flask + GPT-4o 搭建一个移动端友好的"事实采集器":它能把读书笔记、会议纪要、学习日志等杂乱文本自动提炼成有上下文、带来源归属的简洁记忆,并逐条提交到 OMI 的/v2/integrations/{app_id}/user/facts记忆接口。读完本文,你将掌握该工具的环境搭建、Omi App 外部集成配置、AI 提取与规则提取双通道降级逻辑,以及完整的源码级运行原理。
工具定位:为什么需要一个"手动导入"事实采集器
Friend(OMI)的核心产品理念是"AI 看到你的屏幕、听到你的对话并告诉你该做什么"。在这个过程中,除了实时对话流,用户往往还希望把线下积累的知识(读书笔记、会议纪要、个人学习日志、灵感清单)沉淀进 OMI 的记忆体系。Smart Facts Collector 正是这样一个"手动导入"通道:
- 它提供一个移动端优化的 Web 页面(单文件 index.html),在任何手机浏览器上都能打开使用;
- 后端 app.py 接收原始文本,先用 GPT-4o 智能提炼,提炼失败或未配置 API Key 时自动降级到基于正则的规则提取;
- 提取出的每条记忆通过 OMI 外部集成 API 单独提交,写入用户自己的记忆库,供 OMI 后续检索与使用。
从仓库结构看,plugins 目录下还包含大量基于同一套/v2/integrations外部集成 API 构建的插件(如 hume-ai、notifications、zapier 等),Smart Facts Collector 与它们共享相同的集成鉴权与提交模型,是理解 OMI 插件生态最直观的入门示例。
环境搭建:从依赖安装到服务启动
1. 安装依赖
项目依赖集中在 requirements.txt 中,版本被精确锁定:
pip install -r requirements.txt依赖清单(与源码 import 一一对应):
| 依赖 | 版本 | 用途 |
|---|---|---|
Flask | 3.1.3 | Web 服务与/submit-memoriesAPI 路由 |
requests | 2.34.2 | 向 OMI API 提交事实(requests.post) |
openai | 1.3.0 | 调用 GPT-4o 完成智能提取(openai.OpenAI(...)) |
httpx | >=0.23.0, <0.25.0 | 为 OpenAI 客户端提供显式 HTTP 连接池与超时配置 |
2. 配置 OpenAI API Key(AI 提取必需)
在 app.py 中,API Key 从环境变量读取,这是推荐的安全做法:
# Linux/macOS export OPENAI_API_KEY=your_openai_api_key_here # Windows set OPENAI_API_KEY=your_openai_api_key_here源码还通过load_dotenv()支持.env文件方式加载环境变量,因此你也可以在项目目录创建.env文件写入OPENAI_API_KEY=...,避免每次手动 export。若只是本地测试,也可直接修改 app.py 中的占位值,但不建议在生产中硬编码密钥。
值得说明的是,OpenAI 客户端在这里被显式配置为不依赖系统代理:httpx.Client(limits=httpx.Limits(max_keepalive_connections=5, max_connections=10), timeout=httpx.Timeout(timeout=30.0)),即最多 5 个保持连接、10 个并发连接、30 秒超时——这是为了规避常见代理环境导致 OpenAI 调用失败的问题。
3. 在 Omi App 中创建"外部集成"应用
这是与 Friend 生态对接的关键一步,原文档给出如下操作路径:
- 前往 App Store 安装 Omi AI App;
- 在 App 内创建一个新应用,选择"external integration capability"(外部集成能力),并勾选允许"facts"权限;
- 进入设置页,复制你的App ID,并生成private key(私钥);
- 回到 app.py,将生成好的 App ID 填入
APP_ID、private key 填入API_KEY。
源码中的对应常量如下:
APP_ID = "01JPP8Y2PA2YWQPTMDAFHXWX8E" # 替换为你自己的 App ID API_KEY = "get_this_api_key_in_omi_app" # 替换为你生成的 private key其中API_URL会基于 App ID 动态拼出提交地址:
API_URL = f"https://api.omi.me/v2/integrations/{APP_ID}/user/facts"这一 URL 模式在仓库内被多个插件复用,例如 plugins/composio/src/omi_api.py 中的create_fact使用完全相同的f"{API_BASE_URL}/{APP_ID}/user/facts?uid={user_id}"结构,可见它是 OMI 外部集成写入记忆的标准端点。
4. 启动服务并访问
python app.py启动后终端会打印配置摘要(API URL、App ID、GPT-4o 提取是否启用等),服务监听http://localhost:5001。由于app.run(host='0.0.0.0', port=5001, debug=True)绑定了0.0.0.0,同一局域网内的手机也可以直接通过http://<电脑IP>:5001访问,这正是"移动端优化"的实际落地方式。
关键访问方式:页面通过 URL 参数?uid=YOUR_USER_ID识别当前用户,例如:
http://localhost:5001/?uid=YOUR_USER_ID启动日志中也会打印该提示(Access with: http://localhost:5001/?uid=YOUR_USER_ID)。uid会随请求动态传给 OMI API,而不是硬编码在服务端,这是源码中特意注释的改进(USER_ID is now extracted dynamically from requests)。
使用流程:从粘贴文本到记忆入库
- 打开页面,在文本框中粘贴内容——支持各种非结构化格式:读书笔记、会议纪要、个人学习日志、要点列表等;
- 点击"Extract & Submit Memories"按钮;
- 应用依次执行:
- 若 AI 提取开启(默认开启)且已配置 OpenAI API Key,使用 GPT-4o 智能识别与合并事实;
- 若 AI 被关闭或不可用,自动降级为规则提取;
- 展示提取出的每条记忆;
- 逐条提交到 OMI API 并展示提交结果(成功/失败及错误信息)。
前端 index.html 的几个交互细节值得注意:
- 页面顶部显示当前User ID,可通过Change按钮随时切换(会同步更新 URL 的
uid参数); - 若 URL 中没有
uid,页面会弹出醒目警告框("No User ID detected!")并禁用提交按钮,从交互层面强制先完成用户身份配置; - 提交期间按钮被禁用并显示加载动画,防止重复提交;
- 每条提取结果以卡片形式展示,成功为绿色边框 + Success 徽章,失败为红色边框 + Failed 徽章并附错误原文;
- 文本框中预置了示例输入(MrBeast / Made to Stick 两个带标题的要点块),方便首次体验。
源码深读:双通道提取与"记忆合并"策略
app.py 的核心处理链路由/submit-memories路由承载,其设计亮点在于AI 优先、规则兜底、合并上下文三件事。
GPT-4o 智能提取(主通道)
extract_memories_with_gpt是主提取函数,它的系统提示词(system prompt)浓缩了整套提取哲学:
- 按标题合并:识别带标题的内容块(如 "MrBeast"、"Made to Stick"),把该标题下的要点合并为一条记忆,而不是碎片化成多条;
- 来源归属开头:每条记忆以
From MrBeast: ...或From Made to Stick: ...这类短语开头,保留知识来源; - 直白具体、去填充词:明确禁止 "The user has learned that..."、"It appears that..." 等空话,并给出 BAD/GOOD 输出对照示例;
- 长度硬约束:要求每条记忆500 字符以内。
调用参数同样服务于"事实性优先":
response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}], temperature=0.2, # 低温,保证输出更事实、更稳定 max_tokens=2000, # 响应总 token 上限 )返回内容按空行(\n\n)拆分为多条记忆;过短(<20 字符)的记忆被丢弃,超长记忆被截断到MAX_MEMORY_LENGTH = 500字符并追加...。源码中该常量从 2000 调整为 500 并附注释("Reduced from 2000 to 500 characters per memory"),说明设计者有意控制单条记忆体量,以适配 OMI 记忆系统的检索与展示粒度。
规则提取(兜底通道)
当 AI 关闭、API Key 未配置或调用抛异常时,extract_memories_consolidated接手。它同样遵循"合并上下文"思路,核心是一个三级递进的解析策略:
- 识别"标题 + 要点"结构:用正则
([^\n-]+)(?:\n\s*[-*•]\s*[^\n]+)+匹配类似标题\n- 要点1\n- 要点2的段落,将要点以From {title}:前缀合并成一条记忆;若合并后超过 500 字符,则按标题拆分为多条续接记忆; - 退化为段落提取:若无结构化小节,则按空行切分段落,跳过 <50 字符的短段落,超长段落按 500 字符切块;
- 兜底整段切块:若以上都无结果,直接把整段文本按 500 字符切块作为记忆。
这套规则通道保证工具在没有 OpenAI Key 时依然完全可用,只是提取质量从"语义理解"降级为"结构切分"。
提交环节的细节
- 动态用户标识:
user_id = data.get('uid')从请求中提取,缺失时返回 400("Please include 'uid' in your request."); - 记忆元数据:每条记忆以
{"text": memory, "text_source": "other", "text_source_spec": "learning_notes"}提交。text_source为 OMI 约定的来源枚举(如email、social_post、other),text_source_spec记录更细的来源说明(这里是learning_notes);plugins/composio/src/omi_api.py 中同样强调text_source必须是email/social_post/other之一,可在自定义来源时把真实来源写进text_source_spec; - 鉴权头:
{"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}; - 简单限流:从第二条记忆起,每条请求前
time.sleep(0.5),即 0.5 秒间隔,防止瞬时打爆 OMI API(README 的 Technical Details 中明确提到 "Basic rate limiting is implemented to prevent API overload"); - 逐条统计:每条请求独立记录
status_code与success(以 200 为准),最后汇总成功/失败数、总处理耗时,并在终端打印完整请求体与响应体便于排障。
排查指南:端口冲突与 AI 提取失效
原文档的 Troubleshooting 部分归纳了两类最常见问题:
端口冲突
- 修改 app.py 末行的
app.run(host='0.0.0.0', port=5001, debug=True),把port换成空闲端口; - macOS 上 5000 端口常被AirPlay Receiver(隔空播放接收器)占用,可在
系统设置 > 通用 > 隔空播放与接力中关闭该服务(README 给出的是 System Preferences > Sharing 路径)。
AI 提取不工作
- 确认
OPENAI_API_KEY已正确设置(可检查启动日志:若显示GPT-4o extraction: DISABLED则说明未生效); - 查看终端中是否有 API 报错(
extract_memories_with_gpt的异常会打印❌ Error using GPT...并自动降级); - 临时切换到规则提取通道继续使用,或检查是否因代理/网络导致 OpenAI 请求超时(源码已通过显式
httpx.Client规避常见代理问题)。
扩展思考:把"手动导入"接入更大的记忆体系
从源码层面可以推断,这套工具的提交目标user/facts端点与 OMI 的记忆(memories)体系直接打通:前端把use_ai: true与原始文本一并 POST 到本地 Flask 服务,服务端完成提取后逐条写入 OMI,之后这些记忆即可被 OMI 的对话与记忆检索能力所使用。仓库中 plugins/iq_rating/main.py、plugins/composio/src/omi_api.py 分别展示了/memories、/conversations等相邻端点,如果你想进一步扩展,可以让 Smart Facts Collector 支持批量导入、定时同步,或把text_source_spec从固定值改为携带书籍/会议/日期等结构化信息,从而让 OMI 的记忆检索获得更丰富的上下文。
总结
Smart Facts Collector 是一个麻雀虽小、五脏俱全的 OMI 插件示例:它把Flask 服务、GPT-4o 提示工程、正则规则引擎、外部集成鉴权、限流与错误降级完整串在一起。即使不配置任何 OpenAI Key,它也能以规则模式独立运行;配置 Key 后,则能获得"按来源合并、去填充词、500 字上限"的高质量记忆产出。对于想快速把自有知识沉淀进 Friend 记忆体系,或想学习 OMI/v2/integrations集成 API 的开发者,这是仓库中可以直接运行、逐行读懂的最佳起点。
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考