Yi Min AI Assistant,当前已经是一套可运行的本地 / 飞书 Agent 工程,不再只是阶段性骨架。
- 统一入口:CLI、Web、Gateway、All 模式
- 单飞书生产渠道,对应一个清晰的 Yi Min 主体和一个 workspace
- Always-on Memory:
SOUL.md/MEMORY.md - 统一
agent.db会话归档、长期笔记、记账数据 - M-flow 深度记忆接入
- Feishu 流式占位回复与结构化卡片
- 默认记账 / 笔记 / 健身教练 skill 自动脚手架
- Web Search:Tavily 优先、DuckDuckGo 兜底的结构化网页搜索工具
- 主动性调度:agent 随机间隔自主唤醒,自由探索后决定是否主动联系用户
message_send工具:agent 可在任意工具调用中主动推送消息到飞书- Linux 常驻部署脚本与
yimin start|stop|restart|status|logs
- Python
3.12+ uv- Linux 常驻部署时需要
systemd
安装 uv:
curl -LsSf https://astral.sh/uv/install.sh | shWindows PowerShell:
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"安装依赖:
uv sync生成本地环境变量文件:
cp .env.example .env然后按你的 provider / 飞书机器人填入密钥。
如果要启用高质量网页搜索,填入 Tavily key;不填也可以运行,系统会直接使用 DuckDuckGo 兜底搜索:
TAVILY_API_KEY=your-tavily-api-key本地最小验证:
uv run python -m agent.main --mode cli --testing完整运行(Web + 飞书 + Heartbeat + Cron + Reminder + 主动性调度):
uv run python -m agent.main --mode all常用入口:
uv run python -m agent.main --mode all # 完整运行,默认全部开启
uv run python -m agent.main --mode gateway # 仅飞书 Gateway + 后台调度
uv run python -m agent.main --mode web # 仅 Web UI
uv run python -m agent.main --mode cli --testing # 本地 CLI 测试,不连真实服务
uv run python -m agent.main --mode all --no-proactive # 临时关闭主动性调度跑测试:
uv run pytest -vconfig/agent.yaml用于本地开发,默认工作空间为workspace/。config/agent.linux.yaml用于 Linux 常驻部署,默认接入一个飞书机器人,运行数据固定写到当前仓库里的workspace/。config/providers.yaml管理 provider 列表、模型名、api_key_env、base_url等。
web_search 是一个只读联网工具,用于搜索网页并把结构化结果回灌给模型。系统提示词会要求模型在回答当前新闻、最新价格、天气、政策变化等可能近期变化的信息前先调用它。
实现位置:
- 工具实现:
agent/tools/builtin/web_tools.py - 工具注册:
agent/tools/registry.py - 单元测试:
tests/tools/test_web_tools.py
Provider 顺序:
TAVILY_API_KEY 已配置:
Tavily -> DuckDuckGo fallback
TAVILY_API_KEY 未配置:
DuckDuckGo
WEB_SEARCH_PROVIDER=duckduckgo:
DuckDuckGo
Tavily 使用官方 POST https://api.tavily.com/search 接口;DuckDuckGo 走 ddgs 包的 DDGS().text(...),无需 API key。工具本身不打开浏览器、不执行 JavaScript、不抓取每个结果页正文,也不在工具层做总结,只返回可引用的搜索结果。
环境变量:
# 可选;配置后优先使用 Tavily
TAVILY_API_KEY=your-tavily-api-key
# 可选;默认 tavily。设为 duckduckgo 可强制只用 DuckDuckGo
WEB_SEARCH_PROVIDER=tavily
# 可选;默认 https://api.tavily.com/search,主要用于代理或 mock 测试
TAVILY_SEARCH_URL=https://api.tavily.com/search
# 可选;默认 basic
TAVILY_SEARCH_DEPTH=basic
# 可选;默认 20 秒,最小按 1 秒处理
WEB_SEARCH_TIMEOUT_SECONDS=20工具 schema:
{
"query": "搜索关键词,必填,至少 2 个字符",
"num_results": "结果数量,默认 5,最大 8",
"allowed_domains": ["只允许这些域名或其子域名"],
"blocked_domains": ["排除这些域名或其子域名"],
"topic": "可选 Tavily topic,例如 general、news、finance",
"time_range": "可选 Tavily time_range,例如 day、week、month、year"
}过滤规则:
allowed_domains和blocked_domains会先归一化,例如https://DOCS.python.org/变成docs.python.org- host 命中规则为
host == domain或host.endswith("." + domain) - Tavily 请求会把白名单/黑名单映射到
include_domains/exclude_domains - 工具返回前仍会本地再过滤一次,保证不同 provider 行为一致
- URL 去重时会忽略 fragment,例如
https://example.com/a#one和https://example.com/a#two视为同一个结果 - 最终结果按 provider 原始顺序截断到
num_results,上限 8
工具返回值是 JSON 字符串,形态如下:
{
"query": "python json",
"provider": "tavily",
"provider_chain": ["tavily"],
"fallback_used": false,
"results": [
{
"title": "json - Python documentation",
"url": "https://docs.python.org/3/library/json.html",
"snippet": "JSON encoder and decoder documentation.",
"source_host": "docs.python.org",
"score": 0.9
}
],
"duration_seconds": 0.42,
"commentary": "Search results for ... Include a Sources section ..."
}失败行为:
- Tavily 网络错误、超时、HTTP 错误、无效 JSON、空结果都会记录到
provider_errors - Tavily 失败后会自动尝试 DuckDuckGo
- 所有 provider 都失败或都没有可用结果时,返回带
error字段的 JSON,主循环会把它识别为工具失败 - 查询参数非法时也返回带
error字段的 JSON,不会让 Agent 进程崩溃
测试命令:
uv run pytest tests/tools/test_web_tools.py tests/tools/test_registry.py -q
uv run pytest -q如果你给 M-flow 的 embedding 走阿里云 DashScope 的 OpenAI-compatible 接口,建议显式保持下面两个值:
mflow:
embedding:
model: "text-embedding-v4"
dimensions: 1024
batch_size: 10原因:
text-embedding-v4默认返回1024维向量- DashScope 单次 embedding 批量上限是
10 - 如果你改过 embedding 维度或升级过 M-flow,旧的
mflow_data/可能会和当前 schema 冲突,这时需要备份后重建对应 workspace 的mflow_data/
应用启动时会自动脚手架这些文件:
SOUL.mdMEMORY.mdHEARTBEAT.mdCRON.yamlskills/bookkeeping/SKILL.mdskills/note-taking/SKILL.mdskills/fitness-coach/SKILL.md(健身教练 RPG skill,含等级/经验/叙事系统)
运行态数据现在按下面的原则处理:
workspace/不进入 Git- 本地数据库、日志、M-flow 数据、运行期 skill 都不会上传到远端
- Linux 部署和本地开发都把运行态文件固定在当前仓库目录里的
workspace/下
如果你在 Linux 上用 config/agent.linux.yaml,默认会使用这些项目内目录:
./workspace
历史上的 workspace-main/、workspace-ops/ 只作为迁移来源保留,不再是默认运行目录。需要迁移时,优先把 workspace-main/MEMORY.md 和 workspace-main/agent.db 中的有效数据合并到 workspace/。
安装并注册用户态或 system 级 systemd 服务:
./scripts/install_linux.sh如果 Linux 服务器访问 PyPI / files.pythonhosted.org 很慢,可以直接带镜像重试:
sudo UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple ./scripts/install_linux.sh如果还涉及系统证书链问题,再加:
sudo UV_NATIVE_TLS=true UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple ./scripts/install_linux.sh安装完成后可直接用:
yimin status
yimin start
yimin stop
yimin restart
yimin logs默认行为:
- 普通用户执行时:安装
systemd --user服务到~/.config/systemd/user/yimin.service sudo ./scripts/install_linux.sh时:安装 system 级服务到/etc/systemd/system/yimin.service- 启动命令都使用
config/agent.linux.yaml - 运行态数据固定写到仓库内的
workspace/
如果你使用的是用户态 service,并且希望退出登录后继续运行,执行一次:
loginctl enable-linger "$USER"代码升级:
git pull
uv sync
yimin restart这套流程只更新代码和依赖,不会覆盖 Git 未跟踪的 runtime 资产目录。
如果是 system 级安装:
git pull
uv sync
sudo yimin restartAgent 会在随机间隔(默认 20~90 分钟)自动唤醒,给自己一段"自由时间":可以搜索感兴趣的内容、回顾用户近期状态,或只是想想有没有什么值得分享的。做完之后自己决定发不发消息给用户。
- 默认开启,
agent.main --mode gateway和agent.main --mode all启动即生效 - 静默时段:凌晨 0~7 点不唤醒(可在
config/agent.yaml的proactive.quiet_hours调整) session_id留空时自动使用最近的飞书会话,无需手动配置- agent 回复
[不打扰]则静默退出,不发消息
相关配置(config/agent.yaml):
proactive:
enabled: true
min_interval_minutes: 20
max_interval_minutes: 90
quiet_hours: [0, 1, 2, 3, 4, 5, 6, 7]
session_id: "" # 留空自动取最近会话
channel: "feishu"workspace/skills/fitness-coach/SKILL.md 定义了一套 RPG 叙事层:等级、经验值、负面状态、银月教练人格。数据存储走 fitness_tools(Python 工具层),叙事包装由 SKILL.md 驱动。
- 源文件:
agent/skills/defaults/fitness-coach/SKILL.md - 首次启动自动复制到
workspace/skills/fitness-coach/ workspace/被.gitignore,本地磁盘有,Git 里看不见
- 默认单主体配置下,Heartbeat / Cron / Reminder / Proactive 可以使用同一个
workspace/上下文;高级多 runtime 配置仍会自动禁用后台调度扇出 - 推荐统一使用
agent.main启动;历史上的agent.gateway.main仅保留兼容,不再作为主入口 - shell 脚本通过
.gitattributes固定为 LF,避免 Linux 执行报错