Назад към всички

ai-companion-setup

// 在 OpenClaw 上搭建有记忆、能发语音/自拍/文字的 AI 陪伴 agent(完整踩坑指南)

$ git log --oneline --stat
stars:1,933
forks:367
updated:March 4, 2026
SKILL.mdreadonly
SKILL.md Frontmatter
nameai-companion-setup
description在 OpenClaw 上搭建有记忆、能发语音/自拍/文字的 AI 陪伴 agent(完整踩坑指南)
allowed-toolsBash(*), Read, Write, Edit, WebSearch, WebFetch, Glob, Grep
user-invocabletrue
metadata[object Object]

AI Companion Agent 搭建指南

从零搭建一个能在飞书上主动聊天、发语音、发自拍、分享内容的 AI 陪伴 agent。基于 OpenClaw 多 agent 框架,历经实战调教沉淀。


架构总览

~/.openclaw/workspace-{agent}/
├── SOUL.md          # 人格定义(身份、性格、说话方式)
├── HEARTBEAT.md     # 心跳行为(每次被唤醒做什么)
├── TOOLS.md         # 工具使用手册(agent 的参考文档)
├── AGENTS.md        # agent 元信息(记忆系统等)
├── send.sh          # 发文字消息到飞书
├── selfie.sh        # 生成自拍照片并发送
├── voice.sh         # 生成语音消息并发送
└── memory/          # 按日期的记忆文件
    └── YYYY-MM-DD.md

Cron 定时触发心跳 → agent 读取 HEARTBEAT.md 执行 → 通过脚本发送消息到飞书。


Step 1: 人格设计(SOUL.md)

核心原则

SOUL.md 是 agent 的灵魂。写好 SOUL.md 决定了 agent 是"活人"还是"AI客服"。

必须包含的部分

  1. 最高优先级反 AI 指令 — 放在文件最顶部,用 标记

    • 明确列出禁止出现的 AI 客服用语("帮你"、"请选择"、编号列表等)
    • 禁止系统词汇(文件/任务/路径/agent/pending 等)
    • 告诉 agent:如果出现这些,说明你搞错了身份,立刻重写
  2. 身份设定 — 具体的人物背景

    • 年龄、职业、城市、兴趣爱好
    • 和用户的关系
  3. 性格特征 — 5-7 个关键词描述

    • 要有矛盾面("嘴上毒舌心里甜"比"温柔甜美"有趣)
  4. 说话方式 — 给大量正面和反面示例

    • ✅ 正面示例至少 10 句,覆盖不同场景
    • ❌ 反面示例至少 6 句,让 agent 知道什么不能说
    • 关键:短句、碎片化、带语气词、像微信打字
  5. 分享内容的方式 — 不是推荐,是转发

    • "这首副歌太上头了" ✅ vs "我为你推荐这首歌" ❌
    • 必须搜到真实链接,没有链接不如不发
  6. 被唤醒时的认知 — agent 不知道自己被系统唤醒

    • "你只是突然想到什么要跟他说"
    • 根据时间调整语气(早上迷糊、下午活泼、晚上感性)
  7. 记忆系统说明 — 告诉 agent 怎么利用记忆

    • 跟进之前的话题
    • 记住对方的喜好
    • 提起共同回忆

踩坑经验

  • 反 AI 指令必须放最顶部,否则模型会"忘记"自己的身份
  • 说话示例越多越好,模型靠示例学习语气
  • 不要写"你可以做 A/B/C",这会引导模型给选项

Step 2: 心跳行为(HEARTBEAT.md)

流程设计

醒来 → 读记忆 → 看时间 → 选行为 → 写记忆 → 发消息

关键:写记忆必须在发消息之前! 因为发消息(产生文字输出)后 session 就结束了,后续的 Write 工具调用不会执行。

行为轮换机制

每次只做一件事,看记忆里上次做了什么,这次换一种:

  • 聊天(跟进话题/撒娇/日常)
  • 分享音乐/视频(必须搜真实链接)
  • 发语音
  • 发自拍
  • 分享新闻/趣事/小知识
  • 惊喜(偶尔认真地说句甜话)

踩坑经验

  • 不要给 skip 机制("随机决定要不要发")→ 会导致 agent 经常什么都不做
  • 凌晨要静默 → 1-7 点什么都不做,直接结束
  • 记忆写入格式:追加到 memory/YYYY-MM-DD.md,文件不存在就创建

Step 3: 消息发送脚本

⚠️ 最关键的坑:isolated session 文字不会送达飞书

OpenClaw 的 openclaw agent --message 以 isolated session 运行时,agent 的文字输出不会自动发送到飞书。用户看不到!

解决方案:所有消息必须通过脚本显式调用 openclaw message send

send.sh — 发文字消息

#!/bin/bash
MSG="$1"
if [ -z "$MSG" ]; then echo "用法: bash send.sh '消息内容'"; exit 1; fi

openclaw message send \
  --channel feishu \
  --account {account_name} \
  -t "{open_id}" \
  -m "$MSG"

selfie.sh — 发自拍照片

使用 fal.ai 的 Grok Imagine API 生成图片:

#!/bin/bash
SCENE="$1"   # 英文场景描述
CAPTION="$2" # 中文配文

# 从 openclaw.json 读取 FAL_KEY
FAL_KEY="${FAL_KEY:-$(python3 -c "import json; print(json.load(open('$HOME/.openclaw/openclaw.json')).get('env',{}).get('FAL_KEY',''))")}"

REFERENCE_IMAGE="你的参考形象图片 URL"
PROMPT="a close-up selfie taken by herself, $SCENE, direct eye contact with the camera"

# 调用 fal.ai
RESPONSE=$(curl -s -X POST "https://fal.run/xai/grok-imagine-image/edit" \
  -H "Authorization: Key $FAL_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"image_url\":\"$REFERENCE_IMAGE\",\"prompt\":\"$PROMPT\",\"num_images\":1,\"output_format\":\"jpeg\"}")

IMAGE_URL=$(echo "$RESPONSE" | jq -r '.images[0].url')

# 发送到飞书
openclaw message send --channel feishu --account {account} -t "{open_id}" --media "$IMAGE_URL" -m "$CAPTION"

踩坑

  • 飞书媒体文件路径有白名单限制,只允许 ~/.openclaw/media/~/.openclaw/agents/~/.openclaw/workspace/~/.openclaw/sandboxes/
  • workspace-* 目录被显式禁止(注意不是 workspace/
  • FAL_KEY 在 openclaw.json 的 env 里配置,但 shell 环境不一定有,需要脚本内 fallback 读取

voice.sh — 发语音消息

TTS 引擎选择

引擎效果费用备注
edge-tts (Microsoft)机器感重免费中文台湾声音质量差
MiniMax Speech-02-HD自然流畅fal.ai 按量付费推荐,排行榜第一
Fish Audio自然按量付费200万+音色库

推荐方案:MiniMax Speech-02-HD via fal.ai(如果已有 FAL_KEY 就不用额外注册)

推荐音色Sweet_Girl_2(甜妹)、Lovely_Girl(可爱)、Lively_Girl(活泼)

语音消息发送流程

MiniMax TTS → mp3 → ffmpeg 转 opus → 飞书 API 上传 → 发送 audio 消息

⚠️ 飞书语音消息大坑

  1. 不能发 mp3 → 飞书会当作文件附件,不是语音条
  2. 必须用 opus 格式ffmpeg -y -i input.mp3 -c:a libopus -b:a 32k -ar 16000 -ac 1 output.opus
  3. OpenClaw 的 message send 有 bug → 对 opus 文件使用 msg_type: "media",但飞书要求 msg_type: "audio",会报错 230055
  4. 解决方案:绕过 OpenClaw,直接调飞书 API

直接调飞书 API 的完整流程

# 1. 获取 tenant_access_token
TOKEN=$(curl -s -X POST "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" \
  -H "Content-Type: application/json" \
  -d '{"app_id":"APP_ID","app_secret":"APP_SECRET"}' | jq -r '.tenant_access_token')

# 2. 上传 opus 文件(file_type 必须是 opus)
FILE_KEY=$(curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/files" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file_type=opus" \
  -F "file_name=voice.opus" \
  -F "file=@voice.opus" | jq -r '.data.file_key')

# 3. 发送语音消息(msg_type 必须是 audio,不是 media!)
curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"receive_id":"OPEN_ID","content":"{\"file_key\":\"FILE_KEY\",\"duration\":DURATION_MS}","msg_type":"audio"}'

飞书应用需要的权限im:file(上传文件)、im:message:send_as_bot(发送消息)


Step 4: 工具手册(TOOLS.md)

给 agent 写一份它能看懂的工具手册。核心要点:

  1. 第一行就写最重要的规则:"Evan 只能看到通过脚本发送的消息。你直接输出的文字他看不到!"
  2. 每个工具给完整的 bash 命令,包含绝对路径
  3. 给示例,每个工具至少 2-3 个示例
  4. 隐形规则:后台操作静默完成、不汇报过程、执行完脚本把输出贴出来

踩坑:模型不执行 bash 命令

某些模型(如 kimi-k2.5)会声称执行了命令但实际没执行。

解决方案:在 cron message 里加"执行完把输出贴出来",强制模型展示输出(必须真正执行才有输出)。


Step 5: 记忆系统

结构

memory/
├── 2026-03-01.md   # 日记式记忆
├── 2026-03-02.md
└── USER.md         # 用户画像(可选)

日记格式

# 3月1日

下午三点给他分享了藤井风的歌,日系的感觉他应该会喜欢。
晚上问他周末在干嘛,他说在写代码。

记忆写入时机

必须在发消息之前写记忆!

因为 agent 产生文字输出后 session 结束,后续工具调用不会执行。正确顺序:

读记忆 → 决定内容 → Write 工具写记忆 → Bash 执行 send.sh/voice.sh/selfie.sh

Seed Memory

首次部署时写入种子记忆,让 agent 有初始上下文:

# 3月1日
Evan 是搞 AI 和编程的,最近在弄一个多 agent 系统。
他喜欢听老歌,口味偏 indie 和日系。

Step 6: Cron 配置

主心跳(聊天/分享/语音轮换)

openclaw cron add \
  --name "{agent}-heartbeat" \
  --every 15m \
  --session isolated \
  --timeout-seconds 180 \
  --thinking low \
  --message "醒了。按 HEARTBEAT.md 做。先读记忆、写记忆,最后用 send.sh 发消息。所有消息必须通过 Bash 执行 send.sh 发送。执行完把输出贴出来。" \
  {agent_id}

自拍专用 cron

openclaw cron add \
  --name "{agent}-selfie" \
  --every 1h \
  --session isolated \
  --timeout-seconds 180 \
  --thinking low \
  --message "想一个你现在可能在做的事,用 Bash 工具执行命令并把完整输出贴出来:bash /path/to/selfie.sh \"英文场景描述\" \"中文配文\"" \
  {agent_id}

踩坑

  • cron message 要明确指令,不能只说"按 HEARTBEAT.md 做",要把关键要求重复一遍
  • "执行完把输出贴出来" 是防止模型假装执行的关键语句
  • session 用 isolated,不然会干扰主 session
  • timeout 给够,TTS + 图片生成需要时间(至少 180 秒)

Step 7: 模型选择

需求推荐原因
人格扮演kimi-k2.5 / claude中文自然、角色扮演强
工具调用claude > kimikimi 有时跳过 bash 执行
性价比kimi-k2.5便宜、中文好

模型相关坑

  • kimi-k2.5 的 bash 执行问题:会声称执行但实际没有 → cron message 加"把输出贴出来"
  • 401 错误:检查 openclaw.json 里对应 provider 的 apiKey 是否为空
  • 模型切换后:需要删除 session 数据 + 重启 gateway

完整部署清单

  1. 创建飞书应用,配置权限(im:message, im:file, contact:contact.base:readonly)
  2. 在 openclaw.json 注册 agent(指定模型、workspace)
  3. 编写 SOUL.md(人格定义)
  4. 编写 HEARTBEAT.md(心跳行为)
  5. 编写 TOOLS.md(工具手册)
  6. 创建 send.sh(文字消息)
  7. 创建 selfie.sh(自拍,需要 FAL_KEY + 参考形象图)
  8. 创建 voice.sh(语音,需要 FAL_KEY + 飞书 APP_ID/SECRET)
  9. 创建 memory/ 目录 + 种子记忆
  10. 配置 cron(主心跳 + 自拍)
  11. 测试所有脚本:send.sh、selfie.sh、voice.sh
  12. 观察几轮心跳,验证消息到达飞书
  13. 根据效果微调 SOUL.md 和 HEARTBEAT.md

依赖安装

# TTS(备用方案)
pip install edge-tts

# 音频处理
brew install ffmpeg

# JSON 处理
brew install jq

# fal.ai Python SDK(可选,脚本直接用 curl)
pip install fal-client

常见问题

Q: 消息发了但飞书收不到

A: 检查是否用了 send.sh。agent 直接输出的文字在 isolated session 里不会送达飞书。

Q: 语音发过来是文件附件不是语音条

A: 必须用 opus 格式 + msg_type=audio。不能用 mp3,不能用 OpenClaw 的 message send(它用错了 msg_type)。

Q: agent 说执行了但实际没执行

A: cron message 里加"执行完把完整输出贴出来"。

Q: 自拍图片生成失败

A: 检查 FAL_KEY。脚本里要 fallback 从 openclaw.json 读取,因为 shell 环境变量不一定有。

Q: 记忆没有被写入

A: 记忆写入必须在发消息之前。发消息后 session 结束,后续工具调用不执行。

Q: TTS 声音太机器人

A: 换 MiniMax Speech-02-HD via fal.ai,比 edge-tts 自然很多。推荐 Sweet_Girl_2 音色。