尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Open Food Facts Omi 集成:为 Omi 聊天添加免登录的食品信息查询工具

Open Food Facts Omi 集成:为 Omi 聊天添加免登录的食品信息查询工具 Open Food Facts Omi 集成为 Omi 聊天添加免登录的食品信息查询工具【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend导读本文以仓库中plugins/omi-openfoodfacts-app/目录下的 README.md 为核心系统讲解该独立部署的 Omi 插件服务如何基于 Open Food Facts 公开只读 API为 Omi 聊天提供按名称搜索食品、按条形码查询、多商品对比与过敏原核查四大能力。读完本文你将掌握该插件的工具清单与 Omi 应用配置方式、四个聊天工具的请求/响应契约、全部环境变量语义、本地开发与 Railway 部署流程并能结合 main.py 源码理解其数据字段选取、营养数据口径、搜索端点选择与过敏原匹配的底层实现细节。一、插件定位Omi 生态中的轻量无账号查询服务在 Omi 插件体系中omi-*-app/是一批相互独立部署的 FastAPI 服务每个目录都自带main.py、依赖文件与部署描述Dockerfile / Procfile / railway.toml互不依赖、独立上线见 plugins/README.md。omi-openfoodfacts-app正是其中之一它的独特之处在于零账号接入完全基于 Open Food Facts 的只读 API不要求用户连接任何账号不需要 OAuth也不需要 API Key对话内直用通过 Omi 的 Chat Tools 机制用户在聊天中直接发问即可触发查询食品领域专精聚焦包装食品的配料、营养、评分与过敏原信息返回结构化的产品摘要。与需要安装 omi-plugin-sdk 的 webhook 型插件不同本插件是“独立部署的 chat tools 服务”通过暴露.well-known/omi-tools.json清单向 Omi 声明能力属于插件体系中“独立部署服务”这一类。二、功能总览五类开箱即用的食品查询能力依据 README该插件提供以下能力全部由只读 Open Food Facts API 支撑按名称搜索包装食品例如搜索“燕麦奶”按条形码查询单个产品读取包装上的 EAN 码即可命中数据库记录最多五个条形码的产品对比横向比较营养、评分与标签过敏原核查将某条形码或搜索结果第一条中的过敏原、致敏痕迹与配料文本与用户想要避开的食物词表进行匹配返回结构化营养与评级信息每 100g 营养数据、Nutri-Score、NOVA 加工分级、Eco-Score、标签、类别、配料文本以及产品正面小图若存在。全部调用均为只读请求无需 OAuth 或 API Key——这是该项目最重要的集成前提意味着部署成本极低用户可以即刻在对话中体验。三、Omi 应用配置三行填表即可接入在 Omi 平台创建应用时按 README 给出的字段填写字段取值Chat Tools Manifest URLhttps://YOUR-APP.up.railway.app/.well-known/omi-tools.jsonSetup URL留空Setup Completed URL留空其中的YOUR-APP需替换为你实际部署的 Railway 服务域名。由于本插件无需账号绑定因此两个 Setup 相关 URL 都保持空白Omi 在识别到工具清单后即可直接调用。/ .well-known/omi-tools.json这个路径由 main.py 中的get_omi_tools_manifest()直接返回同时提供了/manifest.json别名端点清单内每个工具都声明了名称、描述、HTTP 端点、方法、参数 schema、auth_required: False与status_message这正是 Omi 客户端生成工具调用所需的最小契约。四、Chat Tools 清单四个工具与端点映射README 给出了四个工具的完整映射工具端点用途search_foodsPOST /tools/search_foods按产品名称搜索lookup_barcodePOST /tools/lookup_barcode查询单个条形码compare_foodsPOST /tools/compare_foods最多对比五个条形码check_allergensPOST /tools/check_allergens对某条形码或首个搜索结果核查需避开的过敏原从 main.py 源码看四个端点都遵循统一的 FastAPI 实现模式先解析 JSON 请求体_json_body参数不合法时返回successFalse的ChatToolResponse成功后统一返回{success, message, data}结构。各工具的参数契约如下search_foodsquery必填字符串page_size可选整数默认 5上限 10lookup_barcodebarcode必填字符串可含包装上的数字或整串条形码compare_foodsbarcodes必填字符串数组最多取前 5 个check_allergensavoid必填字符串数组如[milk, peanuts, gluten]外加可选的barcode或query用于定位产品。典型对话示例README 提供了四条可直接体验的示例提示词“Look up barcode 737628064502.”“Find oat milk and show sugar per 100g.”“Compare these cereal barcodes.”“Does this snack list milk, peanuts, or gluten?”这些提示词分别对应lookup_barcode、search_foods、compare_foods与check_allergensOmi 会根据工具描述自动路由到对应端点。五、源码级原理数据口径与实现细节5.1 字段白名单PRODUCT_FIELDSmain.py 中定义了一个固定字段白名单PRODUCT_FIELDS包括code、product_name、generic_name、brands、quantity、categories_tags、labels_tags、ingredients_text、allergens_tags、traces_tags、nutriscore_grade、nova_group、ecoscore_grade、nutriments、image_front_small_url。搜索与条形码查询都会通过fields参数把该白名单传给 Open Food Facts API既缩小了响应体积也避免把无关大字段灌进聊天上下文。5.2 营养口径只认每 100g 数值_summarize_product()输出的nutrition_per_100g只包含能量kcal、脂肪、饱和脂肪、碳水化合物、糖、膳食纤维、蛋白质与盐这 8 个维度。关键在于_nutrient()的实现它只读取带_100g后缀的键如fat_100g并明确拒绝回退到无后缀键——因为无后缀的nutriments键的基准可能是“每份”若将其误标为 per-100g 就是错误数据。这一口径在 test_nutrient_basis.py 中有专门测试守护当某产品只有fat每份而无fat_100g时nutrition_per_100g.fat_g必须返回None而非泄漏的每份数值。5.3 搜索端点为什么用/cgi/search.pl源码注释明确写道/cgi/search.pl才是 Open Food Facts 的全文产品搜索端点而/api/v2/search不是。因此_search_foods()调用GET /cgi/search.pl携带actionprocess、search_terms、search_simple1、json1、page_size与fields参数。test_search_endpoint.py 通过 monkeypatch 捕获实际路径与参数断言请求必须落在/cgi/search.pl且json1从测试层面固定了这一契约。5.4 过敏原匹配否定词的“反误报”处理check_allergens的匹配逻辑在_ingredient_mentions_term()中实现它对配料文本做小写归一化后使用词边界正则匹配并专门处理两类否定表达以避免误报跳过-free/free后缀如gluten-free不命中 gluten跳过no/non-前缀如no peanuts、non-dairy不命中相应词。最终返回的data中包含matches命中的回避词、checked_sources[allergens, traces, ingredients text]与data_note并生成可读消息如{name} may include: milk, peanuts.未命中时则提示用户仍需核对包装标签。5.5 网络层超时、UA 与容错所有上游请求都通过_openfoodfacts_get()发出设置Accept: application/json与自定义 User-Agent超时默认 8 秒由环境变量控制遇到网络异常或非 JSON 响应时返回{error: ...}而不是抛出异常保证聊天工具稳定返回结构化失败信息。为避免阻塞事件循环异步端点经run_in_threadpool把同步 requests 调用放入线程池执行。六、环境变量全部可调项一览变量默认值说明OPENFOODFACTS_BASE_URLhttps://world.openfoodfacts.org生产环境 API 根地址staging 可切换为https://world.openfoodfacts.netOPENFOODFACTS_USER_AGENTOmiOpenFoodFactsApp/1.0附项目仓库地址作为身份标识Open Food Facts 要求应用自报身份便于数据贡献者联系OPENFOODFACTS_TIMEOUT_SECONDS8每个上游请求的超时秒数可调大以适配慢网络PORT8080服务监听端口Railway 与本地运行均使用在 main.py 中前三项在模块加载时通过os.getenv读取BASE_URL还会执行rstrip(/)以兼容带尾斜杠的配置值PORT则用于uvicorn.run的端口绑定。需要注意切换BASE_URL到 staging 域时/cgi/search.pl与/api/v2/product/{code}.json的相对路径均保持不变只是根域名不同。七、本地开发与验证README 给出的本地启动方式为pip install -r requirements.txt uvicorn main:app --reload --port 8080依赖锁定在 requirements.txtfastapi0.104.1、uvicorn0.24.0、requests2.34.2、pydantic2.5.2。服务启动后打开http://localhost:8080/.well-known/omi-tools.json即可检查工具清单是否正常返回。此外还可验证curl http://localhost:8080/health健康检查端点返回{status: ok, service: omi-openfoodfacts-app}。本地调试单个工具时例如搜索“燕麦奶”curl -X POST http://localhost:8080/tools/search_foods \ -H Content-Type: application/json \ -d {query: oat milk, page_size: 3}响应遵循ChatToolResponse结构形如{ success: true, message: Found 3 product(s) for oat milk., data: { query: oat milk, count: 42, products: [ { barcode: 737628064502, name: Oat Milk, brands: ExampleBrand, quantity: 1 L, nutri_score: B, nova_group: 2, eco_score: A, allergens: [gluten], traces: [sesame seeds], labels: [vegan, organic], categories: [plant milks, oat milks], ingredients: Water, oats..., nutrition_per_100g: { energy_kcal: 48, fat_g: 1.5, saturated_fat_g: 0.2, carbohydrates_g: 7.0, sugars_g: 4.0, fiber_g: 0.8, proteins_g: 1.0, salt_g: 0.1 }, image_url: https://.../front_small.jpg, data_note: Open Food Facts data is community contributed and can be incomplete. } ], data_note: Search is capped by this app to reduce API load. } }八、部署到 Railway仓库已为 Railway 一键部署备好全部配置railway.toml使用nixpacks构建启动命令为uvicorn main:app --host 0.0.0.0 --port $PORT健康检查指向/health超时 100 秒失败时自动重启最多重试 3 次Procfileweb: uvicorn main:app --host 0.0.0.0 --port $PORT兼容依赖 Procfile 的平台runtime.txt指定 Python 版本python-3.11。部署成功后将railway.toml中的启动命令与 README 中 Omi App 配置表里的Chat Tools Manifest URL对应起来——把YOUR-APP.up.railway.app换成真实域名填入 Omi 控制台即可完成接入。九、数据边界与防滥用设计重要提示README 明确强调两点数据注意这也是使用本插件时必须传达给用户的边界社区贡献数据的天然局限Open Food Facts 是社区贡献数据库某产品缺失过敏原、营养字段或配料清单并不证明该产品安全或完整。因此插件在工具响应中内置了data_note提示如Open Food Facts data is community contributed and can be incomplete.、Missing Open Food Facts allergen data does not prove the food is safe.避免 Omi 把不完整数据包装成“保证”。搜索限流搜索工具将page_size上限固定在 10防止聊天使用演变成高并发搜索客户端。此外 main.py 的_safe_int()对page_size做了110的钳制compare_foods的_collect_foods_from_body()也只遍历barcodes[:5]从源头限制了单次请求对上游的冲击。十、小结omi-openfoodfacts-app是 Omi 插件体系“独立部署 chat tools 服务”的典型范例以一份.well-known/omi-tools.json清单声明四个只读工具用不超过 10 个环境变量的配置即可完成从本地到生产的一致性运行。它同时示范了两个值得复用的工程实践——只取_100g营养键的严格数据口径与带否定词过滤的配料匹配算法两者均有仓库内测试用例背书可作为后续任何面向消费者数据的聊天工具的设计参考。更多插件体系说明可参见 plugins/README.md插件共享模型定义见 omi-plugin-sdk/README.md。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表