前言

经过好几天的试用和设置,我的 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日

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐