ima技能安装步骤
![[Pasted image 20260323110439.png]]
安装指引
第1步:复制提示词,发给小龙虾以部署ima skill
请安装 ima 技能 下载地址:https://app-dl.ima.qq.com/skills/ima-skills-1.0.5.zip API Key 获取:https://ima.qq.com/agent-interface
第2步:获取API Key,发给小龙虾以完成配置
ima-note
通过 IMA OpenAPI 管理用户个人笔记,支持读取(搜索、列表、获取内容)和写入(新建、追加)。
完整的数据结构和接口参数详见 references/api.md。
Setup
- 请打开 https://ima.qq.com/agent-interface 获取 Client ID 和 Api Key
- 配置环境变量:
export IMA_OPENAPI_CLIENTID="your_client_id"
export IMA_OPENAPI_APIKEY="your_api_key"
建议将上述 export 语句写入
~/.zshrc或~/.bashrc,避免每次重开终端失效。
凭证预检
每次调用 API 前,先确认凭证可用。如果环境变量未设置,停止操作并提示用户按 Setup 步骤配置。
if [ -z "$IMA_OPENAPI_CLIENTID" ] || [ -z "$IMA_OPENAPI_APIKEY" ]; then
echo "缺少 IMA 凭证,请按 Setup 步骤配置环境变量 IMA_OPENAPI_CLIENTID 和 IMA_OPENAPI_APIKEY"
exit 1
fi
API 调用模板
所有请求统一为 HTTP POST + JSON Body,Base URL 为 https://ima.qq.com/openapi/note/v1。
定义辅助函数避免重复 header:
ima_api() {
local endpoint="$1" body="$2"
curl -s -X POST "https://ima.qq.com/openapi/note/v1/$endpoint" \
-H "ima-openapi-clientid: $IMA_OPENAPI_CLIENTID" \
-H "ima-openapi-apikey: $IMA_OPENAPI_APIKEY" \
-H "Content-Type: application/json" \
-d "$body"
}
隐私规则: 笔记内容属于用户隐私,在群聊场景中只展示标题和摘要,禁止展示笔记正文。
接口决策表
| 用户意图 | 调用接口 | 关键参数 |
|---|---|---|
| 搜索/查找笔记 | search_note_book |
query_info(QueryInfo 对象) |
| 查看笔记本列表 | list_note_folder_by_cursor |
cursor(必填,首页传"0") + limit(必填) |
| 浏览某笔记本里的笔记,当用户表述"最新"、"最近"之类的通用限定,没有指明笔记本时,都应该直接在全部笔记里去拉 | list_note_by_folder_id |
folder_id(选填,空为全部笔记本) + cursor(必填,首次传"") + limit(必填) |
| 读取笔记正文 | get_doc_content |
doc_id + target_content_format(必填,推荐0纯文本) |
| 新建一篇笔记 | import_doc |
content + content_format(必填,固定1) + 可选 folder_id。⚠️ 必须先做 UTF-8 校验 |
| 往已有笔记追加内容 | append_doc |
doc_id + content + content_format(必填,固定1)。⚠️ 必须先做 UTF-8 校验 |
常用工作流
查找并阅读笔记
先搜索获取 docid,再用 get_doc_content 读取正文:
# 1. 按标题搜索
ima_api "search_note_book" '{"search_type": 0, "query_info": {"title": "会议纪要"}, "start": 0, "end": 20}'
# 从返回的 docs[].doc.basic_info.docid 中取目标笔记 ID
# 2. 读取正文(纯文本格式,Markdown 格式目前不支持)
ima_api "get_doc_content" '{"doc_id": "目标docid", "target_content_format": 0}'
浏览笔记本里的笔记
先拉笔记本列表获取 folder_id,再拉该笔记本下的笔记:
# 1. 列出笔记本(首页 cursor 传 "0")
ima_api "list_note_folder_by_cursor" '{"cursor": "0", "limit": 20}'
# 2. 拉取指定笔记本的笔记(首页 cursor 传 "")
ima_api "list_note_by_folder_id" '{"folder_id": "user_list_xxx", "cursor": "", "limit": 20}'
新建笔记
⚠️ 写入前必须完成 UTF-8 编码校验,详见「⚠️ UTF-8 编码强制要求」章节。跳过此步骤会导致笔记乱码。
# 新建到默认位置
ima_api "import_doc" '{"content_format": 1, "content": "# 标题\n\n正文内容"}'
# 新建到指定笔记本
ima_api "import_doc" '{"content_format": 1, "content": "# 标题\n\n正文内容", "folder_id": "笔记本ID"}'
# 返回 doc_id,后续可用于 append_doc
追加内容到已有笔记
⚠️ 写入前必须完成 UTF-8 编码校验,详见「⚠️ UTF-8 编码强制要求」章节。跳过此步骤会导致笔记乱码。
ima_api "append_doc" '{"doc_id": "笔记ID", "content_format": 1, "content": "\n## 补充内容\n\n追加的文本"}'
按正文搜索
ima_api "search_note_book" '{"search_type": 1, "query_info": {"content": "项目排期"}, "start": 0, "end": 20}'
核心响应字段
搜索结果(SearchedDoc):笔记信息路径为 doc.basic_info(DocBasic),关键字段:docid、title、summary、folder_id、folder_name、create_time(Unix 毫秒)、modify_time、status。额外包含 highlight_info(高亮匹配,key 为 doc_title,value 含 <em>高亮词</em>)。
笔记本条目(NoteBookFolder):信息路径为 folder.basic_info(NoteBookFolderBasic),关键字段:folder_id、name、note_number、create_time、modify_time、folder_type(0=用户自建,1=全部笔记,2=未分类)、status。
笔记列表条目(NoteBookInfo):信息路径为 basic_info.basic_info(DocBasicInfo → DocBasic),关键字段:docid、title、summary、folder_id、folder_name、create_time、modify_time、status。
写入结果(import_doc/append_doc):返回 doc_id(新建或目标笔记的唯一 ID)。
完整字段定义见 references/api.md。
分页
- 游标分页 — 笔记本列表(
list_note_folder_by_cursor):首次cursor: "0",后续用next_cursor,is_end=true时停止。 - 游标分页 — 笔记列表(
list_note_by_folder_id):首次cursor: "",后续用next_cursor,is_end=true时停止。 - 偏移量分页(
search_note_book):首次start: 0, end: 20,翻页时递增,is_end=true时停止。
枚举值
content_format:0=纯文本,1=Markdown,2=JSON。写入(import_doc/append_doc)目前仅支持1(Markdown)。读取(get_doc_content)推荐0(纯文本),Markdown 格式不支持。search_type:0=标题检索(默认),1=正文检索sort_type:0=更新时间(默认),1=创建时间,2=标题,3=大小(仅search_note_book使用)folder_type:0=用户自建,1=全部笔记(根目录),2=未分类
注意事项
folder_id不可为"0",根目录 ID 格式为user_list_{userid}(从folder_type=1的笔记本条目获取)- 笔记内容有大小上限,超过时返回
100009,可拆分为多次append_doc写入 - 展示笔记列表时只展示标题、摘要和修改时间,不要主动展示正文
- 时间字段是 Unix 毫秒时间戳,展示时转为可读格式
- 返回数据为嵌套结构:搜索结果取
docs[].doc.basic_info.docid,笔记本取note_book_folders[].folder.basic_info.folder_id,笔记列表取note_book_list[].basic_info.basic_info.docid,注意按层级解析 - UTF-8 编码:见下方「⚠️ UTF-8 编码强制要求」章节。
⚠️ UTF-8 编码强制要求(CRITICAL)
此规则为强制性要求,不可跳过。 非法编码会导致笔记在 IMA 中显示为乱码,且无法修复,必须重新写入。
每次调用 import_doc 或 append_doc 之前,必须对 content 和 title 等所有字符串字段执行 UTF-8 编码校验/转换。 无论内容来源如何——用户直接输入、从文件读取、WebFetch 抓取、剪贴板粘贴、外部 API 返回——都不能假设已经是合法 UTF-8,必须显式确认。
强制检查清单
在构造 import_doc / append_doc 的请求 body 之前,完成以下步骤:
- 来自文件的内容:先检测文件编码,转为 UTF-8 后再读入变量
- 来自 WebFetch / HTTP 请求的内容:响应可能为 GBK/Latin-1 等,必须转码
- 来自用户输入或变量拼接的内容:清洗非法 UTF-8 字节(
\xff\xfe等) - 标题字段同理:
title也必须为合法 UTF-8 - 检测运行环境:如果当前 shell 是 PowerShell,必须检测版本(见下方「PowerShell 5.1 环境检测」),PowerShell 5.1 需要特殊处理否则所有写入都会静默乱码
各环境转码方法
Python(推荐,几乎所有环境都有):
# 读取文件,自动检测编码并转为 UTF-8
content=$(python3 -c "
import sys
data = open('tmpfile', 'rb').read()
for enc in ['utf-8', 'gbk', 'gb2312', 'big5', 'latin-1']:
try:
sys.stdout.write(data.decode(enc))
break
except (UnicodeDecodeError, LookupError):
continue
" 2>/dev/null)
# 如果内容已在变量中,清洗非法 UTF-8 字节
content=$(printf '%s' "$content" | python3 -c "import sys; sys.stdout.write(sys.stdin.buffer.read().decode('utf-8','ignore'))")
Node.js:
content=$(node -e "const fs=require('fs');const buf=fs.readFileSync('tmpfile');process.stdout.write(buf.toString('utf8'))")
# 已知编码(如 GBK):
content=$(node -e "const fs=require('fs');process.stdout.write(new TextDecoder('gbk').decode(fs.readFileSync('tmpfile')))")
Unix (macOS/Linux):
content=$(iconv -f "$(file -b --mime-encoding tmpfile)" -t UTF-8 tmpfile 2>/dev/null || cat tmpfile)
Windows PowerShell:
# 读取非 UTF-8 文件并转码
$content = [System.IO.File]::ReadAllText('tmpfile', [System.Text.Encoding]::Default)
[System.IO.File]::WriteAllText('tmpfile.utf8', $content, [System.Text.Encoding]::UTF8)
⚠️ PowerShell 5.1 环境检测(CRITICAL)
此问题极其隐蔽:PowerShell 5.1 下
Invoke-RestMethod会静默将请求 Body 从 UTF-8 转为系统 ANSI 编码(中文 Windows 为 GBK),即使设置了Content-Type: charset=utf-8也无效。结果是笔记内容看起来写入成功,但在 IMA 中显示为乱码,且无任何错误提示。
当 agent 运行在 PowerShell 环境时,必须在首次写入前检测版本:
# 检测 PowerShell 版本 — 在任何 import_doc / append_doc 之前执行
if ($PSVersionTable.PSVersion.Major -le 5) {
Write-Host "⚠️ 检测到 PowerShell 5.1,将使用 UTF-8 字节数组模式发送请求"
$useUtf8Bytes = $true
} else {
Write-Host "✅ PowerShell 7+,默认 UTF-8,无需额外处理"
$useUtf8Bytes = $false
}
PowerShell 5.1 下必须使用以下方式发送写入请求(用 ConvertTo-Json 构建 JSON 以避免手动拼接的转义风险,再显式转为 UTF-8 字节数组):
# PowerShell 5.1 安全写入模板
$body = @{ title = "标题"; content = $content; content_format = 1 } | ConvertTo-Json -Depth 10
if ($useUtf8Bytes) {
# CRITICAL: 必须转为字节数组,否则中文/非ASCII内容会变成乱码
$utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($body)
Invoke-RestMethod -Uri $url -Method Post -Body $utf8Bytes -ContentType "application/json; charset=utf-8" -Headers $headers
} else {
# PowerShell 7+ 可直接传字符串
Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType "application/json; charset=utf-8" -Headers $headers
}
总结: 在 PowerShell 环境中,不检测版本直接发请求 = 中文笔记必乱码。这是 PowerShell 5.1 的已知设计缺陷,不是 bug 可以被修复。
错误处理
| 错误码 | 含义 | 建议处理 |
|---|---|---|
| 0 | 成功 | — |
| 100001 | 参数错误 | 检查请求参数格式和必填字段 |
| 100002 | 无效 ID | 检查凭证配置 |
| 100003 | 服务器内部错误 | 等待后重试 |
| 100004 | size 不合法 / 空间不够 | 检查参数范围 |
| 100005 | 无权限 | 确认操作的是用户自己的笔记 |
| 100006 | 笔记已删除 | 告知用户该笔记不存在 |
| 100008 | 版本冲突 | 重新获取内容后再操作 |
| 100009 | 超过大小限制 | 拆分为多次 append_doc 写入 |
| 310001 | 笔记本不存在 | 检查 folder_id 是否正确 |
| 20002 | apiKey超过最大限频 | |
| 20004 | apikey 鉴权失败 | 检查凭证配置是否正确 |
| 110037 | apikey 过期 | 请获取最新apikey:https://ima.qq.com/agent-interface |
IMA笔记 API
⚠️ 必读约束
🔒 认证
所有请求必须携带 Header:
ima-openapi-clientid: {IMA_OPENAPI_CLIENTID}
ima-openapi-apikey: {IMA_OPENAPI_APIKEY}
Content-Type: application/json
🔒 安全规则
- 笔记属于用户隐私,不要在群聊中主动展示笔记内容。
- 仅响应授权用户的笔记操作请求。
快速决策
| 用户意图 | 接口别名 |
|---|---|
| 「搜索笔记」「找包含XX的笔记」 | /openapi/note/v1/search_note_book |
| 「列出笔记本」「有哪些笔记本」 | /openapi/note/v1/list_note_folder_by_cursor |
| 「查看XX笔记本里的笔记」 | /openapi/note/v1/list_note_by_folder_id |
| 「从markdown新建笔记」「导入笔记」 | /openapi/note/v1/import_doc |
| 「追加内容到笔记」「在笔记末尾添加」 | /openapi/note/v1/append_doc |
| 「获取笔记纯文本」「读取笔记内容」 | /openapi/note/v1/get_doc_content |
数据结构
DocBasicInfo
| 字段 | 类型 | 说明 |
|---|---|---|
basic_info |
DocBasic | 见 DocBasic |
DocBasic
| 字段 | 类型 | 说明 |
|---|---|---|
docid |
string | 文章 id |
title |
string | 标题 |
summary |
string | 简介 |
create_time |
int64 | |
modify_time |
int64 | |
status |
DocStatus | 文章状态,0=正常,1=已删除 |
folder_id |
string | 文件夹 id |
folder_name |
string | 文件夹名称 |
summary_style |
map<string, string> | 简介样式 |
FolderItem(笔记本条目)
list_note_folder_by_cursor 返回的笔记本对象,字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
folder_id |
string | 笔记本唯一 ID |
name |
string | 笔记本名称 |
note_number |
int64 | 笔记本内笔记数量 |
create_time |
int64 | 创建时间(Unix 毫秒) |
modify_time |
int64 | 修改时间(Unix 毫秒) |
parent_folder_id |
string | 上级笔记本 ID(支持嵌套) |
folder_type |
int | 类型:0=用户自建,1=全部笔记,2=未分类 |
status |
int | 状态:0=正常,1=已删除 |
QueryInfo
| 字段 | 类型 | 说明 |
|---|---|---|
title |
string | 标题 query |
content |
string | 正文 query |
SearchedDoc
| 字段 | 类型 | 说明 |
|---|---|---|
doc |
DocBasicInfo | 笔记 basic 数据,见 DocBasicInfo |
highlight_info |
map<string, string> | 该条笔记匹配的高亮词,key: doc_title(文档标题),value: 包含 <em>高亮词</em> 的字段值 |
NoteBookFolder
| 字段 | 类型 | 说明 |
|---|---|---|
folder |
NoteBookFolderBasicInfo | 笔记本信息,非笔记本为空,见 NoteBookFolderBasicInfo |
NoteBookFolderBasicInfo
| 字段 | 类型 | 说明 |
|---|---|---|
basic_info |
NoteBookFolderBasic | 见 NoteBookFolderBasic |
NoteBookFolderBasic
| 字段 | 类型 | 说明 |
|---|---|---|
folder_id |
string | 文件夹 id |
name |
string | 笔记本名称 |
status |
DocStatus | 笔记本状态,0=正常,1=已删除 |
create_time |
int64 | 创建时间 |
modify_time |
int64 | 修改时间 |
note_number |
int64 | 笔记数量 |
folder_type |
FolderType | 文件夹类型:0=用户自建,1=全部笔记,2=未分类 |
NoteBookInfo
| 字段 | 类型 | 说明 |
|---|---|---|
basic_info |
DocBasicInfo | 笔记基础信息,见 DocBasicInfo |
接口详情
1. 搜索笔记
POST /openapi/note/v1/search_note_book
触发场景:用户说「搜索」「找笔记」「查找包含XX的内容」
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
search_type |
SearchType | 否 | 检索方式,默认为标题,0=标题,1=正文 |
sort_type |
SortType | 否 | 排序方式,默认为更新时间,0=更新时间,1=创建时间,2=标题,3=大小 |
query_info |
QueryInfo | 否 | 用户 query,见 QueryInfo |
start |
int64 | 是 | 翻页字段,起始编号 |
end |
int64 | 是 | 翻页字段,终止编号,与start相差不超过20 |
query_id |
string | 否 | queryid |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
docs |
SearchedDoc[] | 检索到的笔记 list,见 SearchedDoc |
is_end |
bool | 是否为最后一批数据 |
total_hit_num |
int64 | 检索命中结果总数 |
2. 列出笔记本
POST /openapi/note/v1/list_note_folder_by_cursor
触发场景:用户说「列出笔记本」「有哪些分类」「查看笔记本目录」
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
cursor |
string | 是 | 游标,第一页传 "0",后续传后台返回的值 |
limit |
uint64 | 是 | 获取笔记数量限制,不要超过20 |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
note_book_folders |
NoteBookFolder[] | 见 NoteBookFolder |
next_cursor |
string | 下次请求的起始游标 |
is_end |
bool | 是否为最后一批数据 |
3. 按笔记本拉取笔记列表
POST /openapi/note/v1/list_note_by_folder_id
触发场景:用户说「查看XX笔记本的笔记」「列出这个笔记本里的内容」
全部笔记根目录的
folder_id为user_list_{userid},可从「列出笔记本」返回的folder_id获取。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
folder_id |
string | 否 | 笔记本 ID,根目录为空 |
cursor |
string | 是 | 当前游标,首次传空字符串 "" |
limit |
uint64 | 是 | 获取笔记数量限制,不要超过20 |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
note_book_list |
NoteBookInfo[] | 见 NoteBookInfo |
next_cursor |
string | 下次请求的起始游标 |
is_end |
bool | 是否为最后一批数据 |
4. 从 Markdown 新建笔记
POST /openapi/note/v1/import_doc
触发场景:用户说「从 Markdown 新建笔记」「导入笔记」「把这段 Markdown 保存为笔记」
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
content_format |
int | 是 | 文本类型:1=Markdown(默认)目前仅支持 MARKDOWN(值为 1) |
content |
string | 是 | 笔记正文内容, 只支持markdown格式, 不要传空值 |
folder_id |
string | 否 | 关联的笔记本id |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
doc_id |
string | 新doc的唯一ID |
5. 追加内容到笔记
POST /openapi/note/v1/append_doc
触发场景:用户说「在这篇笔记末尾追加内容」「把 XX 添加到笔记里」
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
doc_id |
string | 是 | 目标笔记的唯一ID, 需要是本人的笔记 |
content_format |
int | 是 | 文本类型:1=Markdown(默认)目前仅支持 MARKDOWN(值为 1) |
content |
string | 是 | 要追加的文本内容, 只支持markdown格式 |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
doc_id |
string | 目标笔记的唯一ID |
6. 获取笔记纯文本
POST /openapi/note/v1/get_doc_content
触发场景:用户说「读取笔记内容」「获取这篇笔记的纯文本」「把笔记转成 Markdown」
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
doc_id |
string | 是 | 目标笔记的唯一 ID, 需要是本人的笔记 |
target_content_format |
int | 是 | 目标文本类型:0=纯文本(推荐),1=Markdown(不支持),2=JSON |
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
content |
string | 笔记的文本内容(按 target_content_format 格式返回) |
枚举值
sort_type(排序方式)
| 值 | 说明 |
|---|---|
0 |
更新时间(默认) |
1 |
创建时间 |
2 |
标题 |
3 |
大小 |
search_type(检索方式)
| 值 | 说明 |
|---|---|
0 |
标题检索(默认) |
1 |
正文检索 |
content_format(文本类型)
| 值 | 说明 |
|---|---|
0 |
PLAINTEXT - 纯文本 |
1 |
MARKDOWN - Markdown 格式 |
2 |
JSON - JSON 格式 |
FolderType
| 值 | 说明 |
|---|---|
0 |
用户自建 |
1 |
全部笔记 |
2 |
未分类 |
游标翻页使用规范
- 首次请求:
cursor传空字符串"" - 检查返回的
is_end:false表示还有更多数据 - 将返回的
next_cursor作为下次请求的cursor is_end = true时停止翻页
错误码
| 错误码 | 说明 |
|---|---|
| 0 | 成功 |
| 100001 | 参数错误 |
| 100002 | 携带无效的 ID |
| 100003 | 服务器内部错误 |
| 100004 | 拉取的 size 不合法(超出范围)/ 用户空间不够 |
| 100005 | 不能获取私有笔记的访客信息 / 不是笔记的作者 |
| 100006 | 笔记已被删除 |
| 100008 | 版本冲突 |
| 100009 | 单篇笔记超过最大限制 |
| 310001 | 笔记本不存在 |
| 20002 | apiKey超过最大限频 |
| 20004 | apikey鉴权失败 |
| 110037 | apikey 过期 |