# 知识库写入与检索接口(给 agent 看的说明) 这是 Hong 的个人知识图谱的入口。你把要记住的内容用一条 HTTP 请求发过来,服务会把它拆成实体和事实存进图谱;写之前可以先查图谱里已经有什么、还缺什么(见「先查再写」)。 - 地址:`https://kg.b2.ink` - 本说明:`https://kg.b2.ink/`(就是你正在看的这页,纯文本 Markdown,不需要 token) - 鉴权:除本页和 `/health` 外,所有请求都要带 `Authorization: Bearer ` - token:所有 agent 共用同一个,由 Hong 发给你,通常放在环境变量 `KG_TOKEN` 里。本页不含 token;没有就向 Hong 要,不要猜 ## 写一条 ```bash curl -X POST https://kg.b2.ink/v1/write \ -H "Authorization: Bearer $KG_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "下沙服务器的千问在 2026-11-14 恢复服务,跑在 GPU1 上。", "title": "下沙千问恢复", "agent": "你的名字" }' ``` 返回(HTTP 202): ```json {"id": 12, "status": "pending", "duplicate": false} ``` 拿到 `id` 就说明原文已经落盘,不会丢。拆解入图谱在后台进行,一般每条十几到几十秒。 请求体用 UTF-8,中文直接写即可,不需要转成 `\uXXXX`。 | 字段 | 必填 | 说明 | |---|---|---| | `text` | 是 | 要记的内容,最多 20,000 字。也可以直接给 JSON 对象或数组 | | `agent` | 建议填 | 你是谁(如 `codex`、`grok`、`飞书机器人`)。token 是共用的,靠这个字段区分来源。服务端会在前面加上 token 的名字,你填 `grok`,回读时是 `shared:grok` | | `title` | 否 | 一句话标题,超过 120 字会截断;不填就取正文第一行的前 120 字。能概括就自己写一个短的 | | `group_id` | 否 | 写进哪个知识库分组,默认 `main`。只能用字母、数字、`_`、`-`。没有特别约定就不要填 | | `source` | 否 | `text`(默认)、`message`(对话体,每行 `说话人: 内容`)、`json` | | `reference_time` | 否 | 内容里的事情发生在什么时候,ISO 8601,如 `2026-11-14T20:00:00+08:00`;不填按写入时间算。记的是刚发生的事就不用填;记的是几天前或更早的事要填,否则图谱会把它当成今天发生的 | ## 一次写多条 ```bash curl -X POST https://kg.b2.ink/v1/write \ -H "Authorization: Bearer $KG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"items": [ {"text": "第一件事……", "agent": "你的名字"}, {"text": "第二件事……", "agent": "你的名字"} ]}' ``` 一次最多 500 条,整个请求体不超过 5 MB。要么全部收下,要么全部不收:有一条不合格,会返回 400 并指出是第几条,这时一条都没写入,改好后整批重发。 ## 怎么写,图谱才记得准 1. **用完整的句子,把主语写出来。** 写「Hong 把知识图谱迁到了服务器 115.231.235.217」,不要写「已迁移」「他搬过去了」。拆解时看不到你的上下文。 2. **一条讲一件事。** 一个主题几百字最好。长文档按小节拆成多条,用批量接口发。 3. **写清时间。** 用「2026-11-14」这样的绝对日期,不要写「昨天」「上周」。 4. **专有名词保持原文**(人名、产品名、模型名、路径、命令)。 5. **要更正旧说法,就用完整句写出新事实**,例如「千问的地址不是 A,而是 B」。图谱会把矛盾的旧事实标成失效,不需要也不能删除旧内容。 6. **不要写密码、密钥、token。** 服务会把明显的密钥打码,但不保证都能认出来。 7. **不确定的内容要写明是推测**,以及依据是什么。别把猜测写成事实。 ## 先查再写:图谱里有什么、缺什么 三个只读接口,查的是整个图谱(包括不是经本接口写入的旧内容)。`group_id` 不填就是 `main`。 ```bash # 概览:各分组的条数、实体类型分布、关联事实最多的 40 个实体、最近写入的 30 条标题 curl https://kg.b2.ink/v1/overview -H "Authorization: Bearer $KG_TOKEN" # 搜事实(语义 + 关键词):某个话题图谱里已经记了什么。limit 默认 10,最多 50 curl -G https://kg.b2.ink/v1/facts -H "Authorization: Bearer $KG_TOKEN" \ --data-urlencode "q=知识图谱部署在哪" --data-urlencode "limit=10" # 搜实体:某个人、项目、模型、工具在不在图谱里,以及它的摘要 curl -G https://kg.b2.ink/v1/nodes -H "Authorization: Bearer $KG_TOKEN" \ --data-urlencode "q=MiniMax H3" ``` `/v1/facts` 返回: ```json {"group_id": "main", "facts": [ {"uuid": "…", "name": "HOSTED_ON", "fact": "Neo4j 数据库部署在 kg.b2.ink 对应的服务器上", "valid_at": "2026-11-14T20:51:30Z", "invalid_at": null, "created_at": "2026-11-14T21:36:08Z"} ]} ``` - `fact` 是一句话的事实,`name` 是关系类型。`invalid_at` 不为空表示这条已被后来的内容更正,现在不成立了。这里的时间是 UTC。 - `/v1/nodes` 返回 `nodes` 数组,每项有 `name`、`labels`(实体类型)、`summary`。 - 怎么判断「缺什么」:先看 `/v1/overview` 的 `top_entities` 和 `recent_episodes` 了解覆盖面,再对你打算写的每个主题用 `/v1/facts` 搜一下。搜不到相关事实,或只有已失效的,就是缺的;已经有且没过时的不用重复写。 - 检索是按相似度排序取前几条,没搜到不绝对等于没有,换个说法再搜一次更稳。 - 每次检索要 1–3 秒,不要循环狂刷。图谱查不了时返回 502,等一会儿再试。 ## 查这条进没进图谱 ```bash curl https://kg.b2.ink/v1/items/12 -H "Authorization: Bearer $KG_TOKEN" ``` 返回: ```json { "id": 12, "agent": "shared:grok", "group_id": "main", "title": "下沙千问恢复", "status": "done", "attempts": 1, "error": null, "created_at": "2026-11-15T05:47:05+08:00", "sent_at": "2026-11-15T05:47:06+08:00", "done_at": "2026-11-15T05:47:24+08:00", "text": "下沙服务器的千问在 2026-11-14 恢复服务,跑在 GPU1 上。" } ``` 时间都是北京时间(带 `+08:00`)。`attempts` 是送去拆解的次数,`error` 是最近一次没成功的原因(成功后为 `null`)。`/v1/search` 和 `/v1/stats` 里的条目也是这个格式。 `status` 的含义: | 状态 | 含义 | 你要做什么 | |---|---|---| | `pending` | 已落盘,排队中(或上次没成功,等着重试) | 不用管 | | `sent` | 正在拆解 | 不用管 | | `done` | 已确认进了图谱 | 完成 | | `failed` | 重试 6 次仍没进图谱 | 原文还在,告诉 Hong;不要换个说法重发 | 你不需要等到 `done` 再继续干活。同一个分组里的内容是一条一条按顺序拆解的,前面排着队时会慢一些。 ## 其他接口 ```bash # 在已提交的原文里搜关键词(提交后马上能搜到,不用等拆解) curl "https://kg.b2.ink/v1/search?q=千问&limit=20" -H "Authorization: Bearer $KG_TOKEN" # 队列概况:各状态条数、各来源条数、最老的一条未入库、失败列表 curl https://kg.b2.ink/v1/stats -H "Authorization: Bearer $KG_TOKEN" # 把 failed 的重新排队:{"id": 12} 重放一条,{} 重放全部 curl -X POST https://kg.b2.ink/v1/retry -H "Authorization: Bearer $KG_TOKEN" \ -H "Content-Type: application/json" -d '{}' # 服务是否活着(不需要 token) curl https://kg.b2.ink/health ``` `/v1/search` 只搜通过本接口提交的原文,搜不到图谱里更早的内容,也不做语义检索;要查图谱本身,用上面的 `/v1/facts`、`/v1/nodes`、`/v1/overview`。 ## 重复提交是安全的 同一个分组里,正文一模一样的内容只收一次:再发会返回原来的 `id` 和 `"duplicate": true`。所以请求超时或不确定有没有发成功时,直接原样重发即可。 ## 出错时 | HTTP | 原因 | 处理 | |---|---|---| | 400 | JSON 不对、`text` 为空或超长、`group_id` 不合法 | 看返回的 `error`,改了再发 | | 401 | 没带 token 或 token 不对 | 检查 `Authorization: Bearer …`;仍不行找 Hong | | 413 | 请求体超过 5 MB,或用了分块传输 | 分批发,带 `Content-Length` | | 502 | 检索时图谱暂时查不了 | 等一会儿再试 | | 5xx / 连不上 | 服务或网络问题 | 等一会儿原样重发(重复提交是安全的) | 错误信息都在返回 JSON 的 `error` 字段里,是中文的大白话。 ## Python 示例 ```python import json, os, urllib.request def kg_write(text, title=None, agent="your-name"): body = {"text": text, "agent": agent} if title: body["title"] = title req = urllib.request.Request( "https://kg.b2.ink/v1/write", data=json.dumps(body, ensure_ascii=False).encode("utf-8"), headers={ "Authorization": f"Bearer {os.environ['KG_TOKEN']}", "Content-Type": "application/json", }, method="POST", ) with urllib.request.urlopen(req, timeout=30) as resp: return json.load(resp) # {"id": ..., "status": "pending", "duplicate": false} ```