OpenClaw + 飞书 Agent 全面体检与调试指南:从故障排查到安全加固
经过好几天的试用和设置,我的 OpenClaw + 飞书 Agent(多多助理)已经具备了 **TuShare 金融数据查询**、**SearXNG 免费搜索**、**Jina Reader 网页阅读**等能力,还搭建了每日股市新闻自动推送系统。但实际运行中暴露了不少问题,本文完整记录了从系统诊断、Bug 修复、到安全加固的全过程,包含所有命令行操作,可直接复制执行。希望能帮助同样在搭建 AI A
前言
经过好几天的试用和设置,我的 OpenClaw + 飞书 Agent(多多助理)已经具备了 TuShare 金融数据查询、SearXNG 免费搜索、Jina Reader 网页阅读等能力,还搭建了每日股市新闻自动推送系统。
但实际运行中暴露了几个问题:
每天早上 9 点推送的股市新闻不是最新的,甚至有时推送失败
TuShare Token 被 Agent 自己改了存放位置,Gateway 进程读不到
安全配置是否合理?端口是否暴露?
本文完整记录了从系统诊断、Bug 修复、到安全加固的全过程,包含所有命令行操作,可直接复制执行。希望能帮助同样在搭建 AI Agent 工作流的朋友少踩坑。
一、环境背景
|
项目 |
配置 |
|---|---|
|
服务器 |
阿里云 ECS(AliOS / CentOS 系) |
|
OpenClaw 版本 |
2026.3.8 |
|
AI 模型 |
dashscope/qwen3.5-plus(主力)+ 多个 fallback |
|
前端渠道 |
飞书自建 Agent(多多助理) |
|
已部署 Skills |
TuShare v1.0.5、SearXNG Search、Jina Reader |
|
定时任务 |
crontab:每日 9:00 股市推送、12:15 运势推送、17:00 交易执行 |
二、诊断流程:全面「体检」
2.1 第一轮:基础健康检查
首先确认各组件是否正常运行:
# 1. 检查 OpenClaw Gateway 进程
ps aux | grep -i openclaw | grep -v grep
# 2. 检查 Docker 容器状态(SearXNG、Redis 等)
docker ps -a
# 3. 检查已安装的 Skills
ls -la ~/.openclaw/skills/
# 4. 检查 TuShare Token 环境变量(只显示前6位,保护隐私)
echo "TUSHARE_TOKEN=${TUSHARE_TOKEN:0:6}******"
# 5. 检查 SearXNG 是否正常响应
curl -s -o /dev/null -w "HTTP状态码: %{http_code}\n" "http://localhost:8080/search?q=test&format=json"
解读要点:
* Gateway 进程应显示 openclaw-gateway 且状态为 Ssl (后台运行)
* Docker 容器 searxng 和 searxng-redis 都应为 Up 状态Skills 目录应包含 tushare、searxng-search、jina-reader
* SearXNG API 应返回 HTTP 200
2.2 第二轮:深入配置检查
# 1. 查找 OpenClaw 真正的配置文件
find / -name ".env" -path "*openclaw*" 2>/dev/null ls -la ~/.openclaw/
# 2. 查看 Gateway 进程实际加载的环境变量(隐藏敏感值)
cat /proc/$(pgrep -f openclaw-gateway)/environ | tr '\0' '\n' | grep -iE "tushare|token|model|api_key" | sed 's/\(.\{6\}\).*$/\1******/'
# 3. 查看 crontab 定时任务
crontab -l
# 4. 检查 Skills 中是否有定时相关配置
ls ~/.openclaw/skills/*/SKILL.md | xargs grep -l -i "定时\|schedule\|cron\|daily" 2>/dev/null || echo "Skills中未找到定时相关配置"
2.3 第三轮:核心配置文件检查
OpenClaw 的核心配置文件是 ~/.openclaw/openclaw.json,查看时需要隐藏敏感信息:
# 查看 openclaw.json(自动遮掩 Token/Key/Secret)
cat ~/.openclaw/openclaw.json | python3 -c "
import sys, json, re
data = sys.stdin.read() masked = re.sub(r'(\"[^\"]*(?:token|key|secret|password|api)[^\"]*\"\s*:\s*\")([^\"]{6})[^\"]*\"', r'\1\2******\"', data, flags=re.IGNORECASE) print(masked) "
# 检查各 workspace .env 中的 Key(只看变量名)
for f in ~/.openclaw/workspace-*/.env ~/.openclaw/.env; do
echo "=== $f ==="
grep -iE "tushare|token|key|secret" "$f" | sed 's/=.*/=******/'
echo
done
三、问题诊断与修复
3.1 问题一:推送脚本崩溃(TypeError)
现象
查看推送日志发现报错:
tail -100 /root/.openclaw/workspace/logs/a-stock-daily.log
错误信息:
File "a-stock-daily-push.py", line 360, in summarize_news profit_sign = '+' if info['profit_pct'] > 0 else ''
TypeError: '>' not supported between instances of 'NoneType' and 'int'
原因分析
持仓数据通过截图发给飞书 Agent → OCR 识别 → 写入 JSON 的方式更新。当 Agent 无法识别某只股票的盈亏比例时,profit_pct 字段会写入 null (None),而代码直接用 info['profit_pct'] > 0 做比较,没有处理 None 的情况。
经验教训: 凡是来自外部输入(尤其是 AI 识别结果)的数据,都必须做空值兜底处理。AI 不保证每次都能正确识别所有字段。
修复
# 方法一:用 sed 直接修复(快速)
sed -i "s/profit_sign = '+' if info\['profit_pct'\] > 0 else ''/profit_pct = info.get('profit_pct', 0) or 0\n profit_sign = '+' if profit_pct > 0 else ''/" /root/.openclaw/workspace/scripts/a-stock-daily-push.py
修复逻辑:
# 修复前(会崩溃)
profit_sign = '+' if info['profit_pct'] > 0 else ''
# 修复后(安全兜底)
profit_pct = info.get('profit_pct', 0) or 0 # None → 0 profit_sign = '+' if profit_pct > 0 else ''
验证
grep -n 'profit_pct' /root/.openclaw/workspace/scripts/a-stock-daily-push.py | head -20
确认修复行显示 info.get('profit_pct', 0) or 0。
3.2 问题二:市场新闻被过度过滤(20条 → 0条)
现象
日志显示:
市场新闻过滤完成:20 条 → 0 条
所有市场新闻都被 is_recent_news_strict() 函数过滤掉了。
原因分析
两个硬编码导致的问题:
问题 A:持仓股搜索 query 中的日期硬编码
# 第 195 行(硬编码了 2025 年 2 月 3 月) queries = [ f"{name} {code} {dim_keywords} 2025 年 2 月 3 月", # ← 永远搜旧新闻! f"{name} {code} 最新 {dim_keywords} 2026", ]
问题 B:过时年份列表硬编码
# 第 124 行(手动列出了每个月份) outdated_years = ['2020', '2021', '2022', '2023', '2024 年', '2025 年 1 月', '2025 年 2 月', '2025 年 3 月', ...] # ← 随时间推移需要手动更新!
这个列表不会自动更新,时间一久就会误杀有效新闻。
修复
使用 Python 脚本一次性修复两个问题:
python3 << 'PATCH'
filepath = '/root/.openclaw/workspace/scripts/a-stock-daily-push.py' with open(filepath, 'r', encoding='utf-8') as f: content = f.read()
changes = 0
# 修复 A:搜索 query 日期改为动态
old_q = 'f"{name} {code} {dim_keywords} 2025 年 2 月 3 月"'
new_q = 'f"{name} {code} {dim_keywords} {datetime.now().strftime(\'%Y 年 %m 月\')}"' if old_q in content:
content = content.replace(old_q, new_q)
changes += 1
print("修复A完成:持仓搜索日期改为动态")
# 修复 B:outdated_years 改为动态生成
old_outdated = """ outdated_years = ['2020', '2021', '2022', '2023', '2024 年', '2024 年', '2025 年 1 月', '2025 年 2 月', '2025 年 3 月', '2025 年 4 月', '2025 年 5 月', '2025 年 6 月', '2025 年 7 月', '2025 年 8 月', '2025 年 9 月']"""
new_outdated = """ # 动态生成过时年份列表
from datetime import datetime as _dt
_now = _dt.now()
_cur_year = _now.year
_cur_month = _now.month
outdated_years = [str(y) for y in range(2000, _cur_year - 1)] _prev_year = _cur_year - 1
for m in range(1, 13):
if _prev_year == _cur_year - 1 and (_cur_year - _prev_year) * 12 + (_cur_month - m) > 3: outdated_years.append(f'{_prev_year} 年 {m} 月')"""
if old_outdated in content:
content = content.replace(old_outdated, new_outdated)
changes += 1
print("修复B完成:outdated_years 改为动态生成")
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content) print(f"\n共完成 {changes} 项修复")
PATCH
动态日期公式说明:
* datetime.now().strftime('%Y 年 %m 月') → 自动生成当前年月(如2026 年 03 月)
* range(2000, _cur_year - 1) → 自动排除两年前及更早的年份
* (_cur_year - _prev_year) * 12 + (_cur_month - m) > 3 → 保留最近 3 个月的去年新闻,其余标记为过时
修复效果对比
|
指标 |
修复前 |
修复后 |
|---|---|---|
|
市场新闻过滤 |
20 条 → 0 条 |
8 条 → 6 条 |
|
持仓新闻搜索 |
搜到 2025 年旧数据 |
搜当月最新数据 |
|
AI 总结 |
崩溃 TypeError |
正常完成 |
|
飞书推送 |
内容过时或失败 |
推送成功 |
3.3 问题三:TUSHARE_TOKEN 不在 OpenClaw 配置中
现象
Gateway 进程的环境变量中有 OPENAI_API_KEY、BRAVE_API_KEY等,但没有 TUSHARE_TOKEN。
原因分析
OpenClaw Gateway 从 ~/.openclaw/openclaw.json 的 env 节读取环境变量,不会读取
~/.bashrc。而 TUSHARE_TOKEN 只写在了 .bashrc中,所以 Gateway 拿不到。
OpenClaw 的环境变量加载顺序:
1、~/.openclaw/openclaw.json → env 节 (
Gateway 读取)
2、~/.openclaw/.env 文件 (
Gateway 读取)
3、各 workspace 的 .env 文件 (
对应 Agent 读取)
4、~/.bashrc (
Gateway 不读取,仅 shell 会话有效)
5、/etc/environment (
Gateway 不读取,但 cron 脚本可读取)
修复
python3 << 'PATCH2'
import json, os, shutil
token = os.environ.get('TUSHARE_TOKEN', '')
if not token:
print("错误:环境变量 TUSHARE_TOKEN 未设置,请先 source ~/.bashrc") exit(1)
filepath = '/root/.openclaw/openclaw.json'
# 备份原文件 shutil.copy2(filepath, filepath + '.bak.pre-tushare')
print(f"已备份到 {filepath}.bak.pre-tushare")
with open(filepath, 'r', encoding='utf-8') as f: config = json.load(f)
if 'env' not in config:
config['env'] = {}
if 'TUSHARE_TOKEN' in config['env']:
print("TUSHARE_TOKEN 已存在,跳过")
else:
config['env']['TUSHARE_TOKEN'] = token
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
print("✅ 已将 TUSHARE_TOKEN 写入 openclaw.json")
# 验证 print("\n当前 env 节的 key 列表:")
for key in config['env']:
print(f" - {key}")
PATCH2
重启 Gateway 使配置生效
# 同步配置到 systemd 服务
openclaw gateway install --force
# 重启
openclaw gateway restart
# 等待启动完成后验证
sleep 5
cat /proc/$(pgrep -f openclaw-gateway)/environ | tr '\0' '\n' | grep -i tushare | sed 's/=.*/=******/'
注意: 如果修改了 openclaw.json 后只执行 openclaw gateway restart,会提示 Config token differs from service token。必须先执行 openclaw gateway install --force 同步配置,再 restart。
安全检查:确认脚本中无 Token 硬编码
# 搜索脚本中是否有明文 Token(替换为你 Token 的前几位)
grep -n "你的Token前6位\|tushare_token\s*=\s*['\"]" /root/.openclaw/workspace/scripts/a-stock-daily-push.py || echo "✅ 脚本中没有 Token 硬编码"
四、安全加固:6 项全面检查
4.1 检查 SearXNG 端口暴露
# 查看端口监听状态
ss -tlnp | grep 8080
危险信号: 如果看到 0.0.0.0:8080,说明 SearXNG 在所有网卡上监听,可能被公网访问。
安全信号: 应该看到 127.0.0.1:8080,仅本机可访问。
修复:限制 SearXNG 只监听 localhost
# 修改 docker-compose.yml
sed -i 's/- "8080:8080"/- "127.0.0.1:8080:8080"/' ~/searxng/docker-compose.yml
# 验证 grep "8080" ~/searxng/docker-compose.yml
# 重启容器
cd ~/searxng && docker compose down && docker compose up -d
# 确认端口绑定
sleep 3 && ss -tlnp | grep 8080 # 应显示:127.0.0.1:8080
为什么这很重要? SearXNG 的 JSON API 没有认证机制。如果 8080 对公网开放,任何人都能:
1、通过你的服务器发起搜索请求(被滥用为搜索代理)
2、消耗你的服务器带宽和资源
3、通过搜索历史了解你的搜索习惯
4.2 SSH 安全加固
# 查看当前 SSH 配置
grep -E "^(PasswordAuthentication|PermitRootLogin|PubkeyAuthentication|Port)" /etc/ssh/sshd_config
# 查看暴力扫描记录 lastb | head -10
# 查看最近成功登录
last -5
修复:PermitRootLogin 改为 prohibit-password
sed -i 's/^PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config systemctl restart sshd
SSH 安全配置速查表:
|
配置项 |
推荐值 |
说明 |
|---|---|---|
|
PasswordAuthentication |
no |
禁用密码登录,只允许密钥 |
|
PermitRootLogin |
prohibit-password |
root 只能用密钥登录 |
|
PubkeyAuthentication |
yes |
启用公钥认证 |
|
MaxAuthTries |
3 |
限制尝试次数 |
4.3 敏感文件权限检查
# 检查关键文件权限
ls -la ~/.openclaw/.env
ls -la ~/.openclaw/openclaw.json l
s -la ~/.openclaw/credentials/
ls -la /etc/environment
安全标准:
|
文件 |
推荐权限 |
说明 |
|---|---|---|
|
~/.openclaw/.env |
600(-rw-------) |
仅 root 读写 |
|
~/.openclaw/openclaw.json |
600(-rw-------) |
仅 root 读写 |
|
~/.openclaw/credentials/ |
700(drwx------) |
仅 root 读写执行 |
|
/etc/environment |
600(-rw-------) |
仅 root 读写 |
如果权限过于宽松,执行:
chmod 600 ~/.openclaw/.env ~/.openclaw/openclaw.json /etc/environment
chmod 700 ~/.openclaw/credentials/
4.4 Token 存储冗余清理
检查 .bashrc 中是否还残留 Token:
grep -n -iE "tushare|token|key|secret|API" ~/.bashrc
如果有 Token 相关行(非注释),建议清理。Token 应统一存放在 openclaw.json 中。
合理的 Token 存储架构:
┌─────────────────────────┐
│ openclaw.json → env │ ← OpenClaw Gateway 读取(主要来源) ├─────────────────────────┤
│ /etc/environment │ ← cron 定时脚本读取(辅助) ├─────────────────────────┤
│ ~/.bashrc │ ← 仅 shell 会话(建议清理) └─────────────────────────┘
4.5 Docker 网络安全
# 查看容器端口映射
docker port searxng
# 查看容器网络配置 docker inspect searxng | grep -A 5 "Networks"
确认 SearXNG 运行在独立的 Docker 桥接网络(searxng_default)中,与宿主机网络隔离。
4.6 全端口暴露面检查
ss -tlnp | grep LISTEN
理想状态:
|
端口 |
服务 |
绑定地址 |
安全评估 |
|---|---|---|---|
|
22 |
SSH |
0.0.0.0 |
|
|
8080 |
SearXNG |
127.0.0.1 |
|
|
18789 |
OpenClaw Gateway |
127.0.0.1 |
|
|
18791/18792 |
OpenClaw 内部 |
127.0.0.1 |
|
判断标准: 除了 SSH(端口 22)之外,所有服务都应该绑定在 127.0.0.1。如果看到其他服务绑定在 0.0.0.0,说明存在暴露风险。
五、Agent 搜索架构详解
完成所有修复和安全加固后,整个 Agent 的搜索架构如下:
5.1 三个搜索 Skill 的分工
|
Skill |
触发场景 |
数据源 |
速度 |
费用 |
|---|---|---|---|---|
|
SearXNG |
"搜索" "查一下" "帮我找" |
localhost:8080 → 百度/Bing |
~1秒 |
免费 |
|
Jina Reader |
"阅读" "读取网页"(需显式指定) |
r.jina.ai 云端 API |
~15秒 |
免费额度 |
|
TuShare |
股票代码、行情、财务数据 |
api.waditu.com(HTTPS) |
~2秒 |
Token 额度 |
5.2 搜索调用流程
用户在飞书发消息
↓ Agent(qwen3.5-plus)分析意图,匹配 Skill 触发词
↓ ┌──────────────────┬──────────────────┬──────────────────┐
│ SearXNG Skill │ Jina Reader │ TuShare Skill │
│ "搜索/查一下" │ "阅读/读取网页" │ "股票/行情" │ ├──────────────────┼──────────────────┼──────────────────┤
│ curl localhost │ curl r.jina.ai │ python3 tushare │
│ :8080/search │ /目标URL │ 调用 API │ ├──────────────────┼──────────────────┼──────────────────┤
│ 返回搜索列表 │ 返回 Markdown │ 返回股票数据 │ └──────────────────┴──────────────────┴──────────────────┘
↓
Agent 整理信息,回复用户
5.3 每日推送流程(独立于 Skill)
crontab 每天 09:00 触发
↓ python3 a-stock-daily-push.py
↓
1. 加载持仓 JSON(来自飞书截图 → Agent OCR)
2. Bocha API 搜索持仓股新闻
3. 抓取财联社电报实时快讯
4. Bocha API 搜索市场新闻
5. 过滤 + 去重 + 时效性验证
6. SiliconFlow AI(Qwen2.5-72B)总结
7. openclaw message send → 推送到飞书
六、验证与测试
6.1 手动测试推送脚本
python3 /root/.openclaw/workspace/scripts/a-stock-daily-push.py 2>&1 | tail -30
正常输出应显示:
【1/6】加载持仓数据... 共 3 只持仓股/基金
【2/6】搜索持仓股新闻(严格审核)... 持仓新闻搜索完成:共 X 条
【3/6】搜索市场新闻... 财联社电报抓取完成:共 X 条 市场新闻搜索完成:共 X 条
【4/6】过滤市场新闻... 市场新闻过滤完成:X 条 → X 条(不应为 0)
【5/6】新闻汇总:X 条 【6/6】AI 严谨总结...
总结完成 正在推送到飞书...
✅ 推送完成
6.2 验证持仓数据完整性
# 查看最新的持仓 JSON
cat $(ls -t /root/.openclaw/workspace/holdings/*.json | head -1) | python3 -m json.tool
重点检查:
* 每只股票的 code 字段是否完整(如300274)
* profit_pct 字段是否有值(不为null)
* name 是否正确(特别是 ETF 的全称)
七、踩坑总结与经验
7.1 踩坑记录
|
坑 |
表现 |
根因 |
解决方案 |
|---|---|---|---|
|
Token 放错位置 |
Gateway 读不到 TUSHARE_TOKEN |
写在
.bashrc 而非openclaw.json |
写入
openclaw.json 的env 节 |
|
日期硬编码 |
搜到旧新闻 / 新闻全被过滤 |
脚本中写死了年月 |
改为
datetime.now() 动态生成 |
|
AI 识别数据为空 |
脚本崩溃 TypeError |
OCR 未识别盈亏百分比 |
info.get('profit_pct', 0) or 0 兜底 |
|
端口绑定 0.0.0.0 |
SearXNG 可能被公网访问 |
docker-compose.yml 默认配置 |
改为
127.0.0.1:8080:8080 |
|
Gateway 配置不同步 |
重启后新配置不生效 |
systemd 缓存旧配置 |
先
install --force 再restart |
|
PermitRootLogin yes |
暴力扫描风险 |
默认配置未加固 |
改为
prohibit-password |
7.2 核心经验
1、永远不要硬编码日期。 用datetime.now() 动态生成。今天写的2026年3月到了 4 月就是 Bug。
2、外部输入必须做空值兜底。 AI 识别、API 返回、用户输入——任何外部数据都可能缺字段。用
.get(key, default)或 or default 处理。
3、弄清配置的加载顺序。 OpenClaw 读 openclaw.json,不读 .bashrc。弄错了就会出现「我明明设置了,为什么不生效」的困惑。
4、Docker 端口默认绑定 0.0.0.0。 写 docker-compose.yml 时,养成写 127.0.0.1:端口:端口 的习惯,除非你确实需要对外暴露。
5、修改配置后要同步 + 重启。 OpenClaw 的正确流程是:修改 openclaw.json → openclaw gateway install --force → openclaw gateway restart。
八、完整修复清单
结语
搭建 AI Agent 工作流,「能跑起来」只是第一步,真正的挑战在于让它稳定、安全、可维护地运行。这次全面体检发现的问题,从硬编码日期到端口暴露,都是容易忽略但影响重大的细节。
希望这份指南能帮助同样在折腾 OpenClaw + 飞书 Agent 的朋友,少走一些弯路。如果你也在搭建类似的系统,欢迎交流!
作者:海风 | 环境:阿里云 ECS + OpenClaw 2026.3.8 + 飞书 | 日期:2026年3月13日
更多推荐



所有评论(0)