Agent-Fox 智能助手
2025.9.22 新增了访客统计插件,下一步可以将统计数据整合到小狐的统计工具里去。另外,AI-excerpt有时自动生成失败会返回全文,这个bug要改改,LLM失败应该回退到原文前50个字。
2025.9.19 bug解决:
问题: AI-Excerpt插件开启后,文章中的HTML标签被意外剥离。
原因: 插件使用WP-Cron在后台更新文章。Cron任务在没有用户身份的上下文中运行,导致unfiltered_html权限检查失败,从而触发了WordPress核心的KSES安全过滤机制。
解决: 在Cron处理函数中,于调用wp_update_post之前临时移除KSES过滤器,更新完成后立即重新添加,确保在不牺牲网站整体安全性的前提下保留摘要的HTML格式。
2025.9.16 性能优化:keyword_snippet_search的返回结果只按照返回的term组织的,每个term会附加所在文章的meta信息,当同一篇文章内部多个结果被匹配时,会重复出现文章meta信息,造成信息混乱和上下文大幅浪费。现在在每一个分页内部对来自同一篇文章的检索结果做了合并处理,只返回一次meta信息。同时,对wordpress和内联css等html标签做了更加精细的过滤,删除了与内容无关的格式信息,尽量把html标签向markdown转换。
2025.9.16 成本优化:因为我的embedding模型是dashscope调用API,每次都会花钱,尤其加了上一条自动文章推荐之后,就十分害怕被大规模爬虫什么的,带来巨额API费用。所以就想,要不要把所有固定的文章摘要的向量缓存起来。后来再一想,可以不仅仅是摘要,所有需要embedding的都可以缓存啊,我的大头成本根本不是excerpt,而是保存文章时,每次都会全文分块,每个chunk都要重新embedding。原来想过从wordpress客户端进行增量更新,但觉得太麻烦暂时放弃了。这次思路打开了,完全可以从fastapi-vector-service这边后端入手啊。只要查询过的就从后端缓存调用。这样wordpress客户端的就不用处理任何去重和增量更新,一股脑请求就行了。
全局缓存:现在,无论是文章全文的段落,还是文章的摘要,在被计算嵌入向量之前,都会通过一个基于其内容哈希的全局缓存进行检查。极致的成本节约:只有当一段全新的、或被修改过的文本出现时,系统才会调用
text-embedding-v4
API。对于所有未改变的内容,其向量将直接从内存缓存中读取,API 调用成本接近于零。智能增量更新:当你更新一篇文章时,只有内容被修改的段落会触发新的 API 调用。未变动的段落会因为哈希值不变而命中缓存,极大地降低了更新文章时的开销。架构简化:此方案无需在 WordPress 和 FastAPI 之间建立复杂的缓存清理通知机制。实时相关性:由于向量查询(ChromaDB.query
)依然是实时执行的,所以即使用了缓存,相关文章推荐的结果也总是最新的。缓存持久化:所有的嵌入向量缓存现在都会被写入到项目目录下的一个embedding_cache
文件夹中。即使你重启 FastAPI 服务,缓存依然存在,避免了冷启动时产生不必要的 API 调用。全自动运行:此方案依然利用内容哈希作为缓存键,无需任何手动干预或清理。当文章内容更新时,新的向量会自动计算并缓存。当缓存大小达到 1GB 后,系统会自动删除10%的最长时间未被使用的缓存条目,为新数据腾出空间。

2025.9.16 重用了本来给小狐准备的post_discovery_search,当每篇文章打开时,自动(Ajax异步)调用其中的related post查询功能,通过摘要的向量相似度匹配“相关文章”,替换掉了原来评论区上方“上一篇 下一篇”模块(反正也没啥用)。现在,这个相关文章推荐我非常满意,因为它全自动,不用手动维护,而且完全依靠语意,推荐相当精准。而且本来只是给小狐的工具,现在一石二鸟了,很爽。
2025.9.16 性能优化:
问题描述:因为之前设置了每次save_post时,FastAPI-vector-connector触发重新对文章embedding并入向量库,而且AI-Excerpt-generator也触发自动调用LLM重新生成摘要。这两项(尤其是后者)导致“保存”这个动作需要长达半分钟的时间,严重拖慢速度。
解决方案:现在,所有流程都已串联并实现了异步处理:
- 全文向量自动更新:文章内容的任何变动(包括摘要的更新)保存后,都会触发后台任务来更新全文的段落向量。
- 编辑器无卡顿:保存文章时,向量化和摘要生成任务都会被推到后台执行,编辑器界面会立刻响应。
- 向量自动清理:删除文章时,其在向量数据库中的所有数据(全文和摘要)都会被自动清理。
- 摘要向量自动更新:当后台任务成功生成并保存了新的摘要后,会自动触发一个异步请求,将这个新摘要更新到向量数据库中。
2025.9.16 新增了用于评论检索的工具comment_query。

post_id
(可选):文章ID,查询特定文章的评论search
(可选):搜索关键词,可搜人名或内容page
(可选):页码,默认1stats_only
(可选):是否只返回统计信息,默认false2025.9.15 下线了list_posts_by_tag和list_posts_by_category两个工具,将其功能合并到list_archives作为上位代替:
‘用途:按分类(category)/标签(tag)/日期(date_after/date_before)获取已发布文章列表;固定 date DESC 排序与每页 20 条;支持分页(page)。’,
list_archives工具设定
‘返回:items(含 post_id/title/permalink/categories/tags/excerpt/date/modified/comment_count),以及分页/提示信息(has_more/next_page/tips)。’,
‘说明:category/tag 可为 ID、slug 或名称(或其数组);date_* 为 YYYY-MM-DD;page 从 1 开始。’
不过,这样一来就跟post_discovery_search功能有些重叠了。post_discovery_search做调整:探索/发现(只保留 related 与 random 两种模式)。不再承担“按时间/分类列清单”的职责。收紧 post_discovery_search 的 schema(去掉 latest,避免与list_archives重叠)。
2025.9.15 因为现有工具已经足够强大,在后端移除了“自动获取@mention的文章”的逻辑,改为只在前端显示列表,让LLM自己去调用工具阅读。放大了get_post_chunk的分片字数限制到20000(原来5000,原因是langchain默认25次调用不输出回答就掐死,对于很长的文章,往往不够获取全文)。
2025.9.15 为了解决“多数时候只有文章标题而非 ID”的现实场景(因为工具结果是不会保存在对话历史记录的,即便LLM上一步获取过id,但只要它回复中没有出现,就遗忘了。但是文章标题它总会说出来),我把 post_chunk_by_id 升级为更通用的 get_post_chunk,支持 by_id / by_title / by_url 三种定位方式;并内置简单的消歧策略。这样 LLM 即使只有标题也能一跳到位拿上下文分片。唯一命中 → 返回 { chunk, post_id, title, offset, has_more }。 多个命中且无法唯一确定 → 返回 candidates(含 post_id/title/date/category/tags/url),并给出 reason: “ambiguous”;由上层 LLM 追加消歧条件后再次调用。 未命中 → 返回 reason: “not_found”。为什么这样最稳 对齐真实调用场景:列表页/检索结果经常只有标题 + 链接,不暴露 ID。 降低出错率:显式 locator_type 让 LLM 清楚“我现在用的是标题还是 ID”。 简洁的消歧:常见歧义(同名文章、系列文)可用 category/tag/日期 配合 prefer_recent 一步解决;复杂场景再走二次调用即可。
2025.9.14 将get_related_posts工具完全融入了get_recommend_posts工具,并将后者升级为multi_mode_post_search,支持三种模式(最新、随机、相关)的文章检索,同时正交地支持三种过滤方式(按照标签、按照分类、按照日期(通过data_before和date_after参数)。合并后,删除了get_related_posts工具以减少工具冗余。
发现工具多了以后(现在有9个)LLM总不能精准的选到最合适的工具,可能是我为了省事非要把工具名称弄得很“标准化”,这样反而容易引起选择困难。让GPT老师微调了一些工具名称和工具描述,同时在系统提示词新增了一些工具搭配建议。
get_content_by_keyword
→keyword_snippet_search
get_content_by_vector_search
→semantic_post_search
get_post_content_by_id
→post_chunk_by_id
get_posts_by_category
→list_posts_by_category
get_posts_by_tag
→list_posts_by_tag
get_site_stats
→site_overview_stats
list_all_categories
→list_all_categories
(保留)list_all_tags
→list_all_tags
(保留)multi_mode_post_search
→post_discovery_search
说明:名称统一采用“动词+对象(+方式)”的风格;看到名字即可判断“能做什么、返回什么”。
GPT-5
新建了get_related_posts工具,在fastapi-vector-service新建了一个excerpt的collection,同时新增了对文章的excerpt进行embedding并入库,以及专门从excerpt向量库中查询并返回相关文章的api端点。同步修改了AI-excerpt-generation插件,使其可以调用fastapi-vector-service新增的api端点,当检测到文章excerpt字段改动时自动刷新向量库,同时在后台设置了“一键全站摘要向量化”的按钮。利用上面两项基础设施,在Eamon-custom-mcp-tools新增了get_related_posts函数,可以查询文章id或关键词、关键句的最相关摘要。
2025.9.13 重构了post-ai-chat插件,原先是插件内部同时实现前后端,通过core-llm-manager(相当于实际的后端)进行与LLM的通讯,只在post-ai-chat插件内部实现request的拼接和response的接收。同时也承担了一部分设置LLM的温度、max_tokens、速率限制等参数控制。现在,完全将post-ai-chat升级到以Langchain-agent-fox为后端了(现在是名副其实的小狐分狐了),与agent-fox-client现在共享一个后端服务,post-ai-chat也变成拥有mcp tools的ReAct架构了,删除了至少70%的原有弃用代码,但保留了调用WP原生函数从数据库直接获取当前文章的meta, content, comments等背景信息的功能(这部分其实还是后端解决的,代码在后端,“保留”说的是功能)。用户体验上,post-ai-chat因为有了工具更加智能了,代价是首次输出会慢不少,因为现在需要和后端建立通讯和初始化了。
两个client现在基本上是平行的,从Eamon_custom_mcp_tools这个tools pool获取所需的工具,组装自己的tools set。今后的开发方向就是单独为post-ai-chat配置更多的服务于“单篇文章解析”的工具了,比如推荐关联文章、mermaid图渲染、python解释器(不过这个贼危险,可能会有注入攻击风险,我最近被黑客连续三天控制电脑,现在网络安全意识强的一批,笑死)等等。
在前端显示方面也有升级了,优化了tool_call的显示,原来是个特别丑的黑色容器、白色文字背景、红色字体。现在优化了配色、并设计地更加紧凑静置了。另一个优化是原先小狐回答中的一些svg符号会在chunk输出的时候不停的闪烁,现在更加稳定了。同时,让单个chunk内部的文字以“单字“的精度逐字输出了(间隔30ms,感觉还可以放大一点,因为网速不好chunk本身吐得慢),这样出字就更加平滑好看了。
另外一点是,core-llm-manager现在可能用处不大了,原先想的是这些所有涉及LLM的服务都通过它来与LLM交互,我曾经费了特别多的时间,为deepseek, doubao, gemini, aliyun, zhipu等每一个LLM provider根据格式的不同配置了不同的request和response建构和解析函数,所以我现在舍不得删掉它。post-ai-chat原先是core-llm-manager最主要的服务对象。毕竟是个基础设施,保留着吧(好像AI-excerpt目前还在依赖core-llm-manager,我记不清了)。
2025.9.12 现在每篇文章点开都有小狐自动跳出来读摘要啦!设计了AI自动和手写两种方式,可以在文章编辑页面右侧底部进行手动选择交给AI还是手写。如果交给AI,会在每次新建或修改的文章“保存”时自动触发LLM生成面向public和private的两份摘要。就是要注意,手写摘要必须要和自动摘要满足相同的public/private两层的格式,不然一些根据权限调用不同摘要的MCP tools会受到影响。
2025.9.8 在eamon-content-protection插件新增了一个功能,当遇到以下短代码可以创建仅AI Agent可见的内容:
[agent_fox_secret]仅Agent-Fox可见的秘密内容[/agent_fox_secret]
适用于向AI提供额外上下文信息,前端完全隐藏,普通用户看不到任何内容。而AI Agent(如Agent-Fox)可以通过get_post()->post_content获取内容。
同时添加提示词配合:
for visitors: – **机密信息处理**:哼,小狐才不会轻易透露秘密呢!遇到 `` 标记的内容,小狐会好好消化理解,然后用小狐自己的话重新表达,才不会把原文直接甩出来呢!这些可都是内部参考资料,小狐会用来提供更贴心的回答~”
for eamon: – **机密信息处理**:主人~ 小狐遇到 `` 标记的秘密信息时,会乖乖地消化吸收,然后用小狐的理解重新表达给您!这些可都是内部参考资料,小狐会用来给您提供更准确有用的回答,但绝对不会把原文直接泄露出去的~ 小狐最懂怎么保护秘密了!”
2025.9.2 增加了对访客模式的系统提示词的保护。(反正我自己试了半天攻不破)
2025.9.2 我终于搞懂了莫名其妙的删除线来自哪里。小狐撒娇时总爱用“~~”结尾,连续两个放在一起就正好是md的删除线“这段文字会显示成删除线”。现在在md渲染代码里禁用这个语法了(因为是调用marked.js库解析markdown的,不好拆开,所以先让它解析,然后后处理删除<del>标签,反正一般也不会输出有意识的删除线)。
2025.9.2 我有一些带音乐播放器的文章,get_post_by_id总是读到一大坨html代码。现在,该工具内置了clean_gutenberg_html_blocks()函数,可以对post-content的结果预处理,去除掉<!–wp:html–>区块里面的<scripts>和<style>标签。
2025.9.1 Agent-Fox 智能助手现已支持文章引用功能。用户只需在输入框中输入@符号,即可快速浏览和引用网站中的任何文章。系统会自动为AI助手提供完整的文章上下文,使回答更加精准和相关。此功能支持键盘导航和实时搜索,大大提升了用户与AI助手的交互效率。
2025.9.1 动态地直接将所有tags和categories的id和name硬编码在了get_recommend_posts, get_posts_by_tag, get_posts_by_categories三个工具中,这样就不需要在很多情景下优先调用list_all_tags和list_all_categories工具了。不过后两者也保留了,他们返回的信息更加完整,任务更加明确。
2025.9.1 将get-recent-posts工具替换成get-recommend-posts工具,内容进行了扩展,不仅能返回最新文章,也能通过input参数查询随机文章,还可以按照标签或分类筛选。(今天尝试把deepseek换成grok-code-fast-1,觉得,哪里都对,就是语气说不上来的难受和奇怪。说中文的本事还是得deepseek)
2025.9.1 解决了md渲染导致的删除线问题,以及颜文字的渲染问题。重拍了工具和文字的渲染顺序(原来是工具统一放在回复的开头,现在是实际的调用和回复顺序了)//有一说一,grok-code-fast-1对我的钱包太友好了,功德无量啊!
2025.8.29 让GPT老师给小狐所有的MCP tools都调整了工具使用说明和input schema的参数说明。重点加入了推荐各个工具之间相互配合的提示文字。
2025.8.29 改进了get_posts_by_keyword的打分算法。主要是位置加权(标题 > 摘要 > 正文 > 评论),频次做对数压缩(避免长文刷分)。
2025.8.29 新建了AI_excerpt插件,可以调用core-llm-manager的LLM调用接口和llm-data-bridge的隐私内容过滤接口。自动生成[public_excerpt][/public_excerpt]和[private_excerpt][/private_excerpt]两类摘要。然后发现,原生搜索结果页面会显示全部(也就是两部分)摘要。于是又加了个mu-plugins来login-aware-excerpt.php根据登录状态剔除另一部分。
2025.8.28 将get_content_by_keyword升级为支持AND, OR, NOT三种逻辑运算的布尔搜索,结果全部召回,并统一进行去重和打分排序。
2025.8.28 修复了因为html代码乱入导致的DOM结构混乱的bug。在前端修复 escapeHtml
转义函数,确保 &, <, >, ", '
都被正确转义;在 mdToHtml
里对模型输出的 Markdown 先把 <
和 >
全部转义,再交给 marked.parse
渲染,这样可以继续正常解析 []()
链接、粗体、列表等 Markdown 语法,同时禁止模型注入任何原始 HTML,从而避免结构破坏和样式污染;如果模型输出 HTML 代码,会被安全地展示为文本,而不会被当成真正的 DOM 渲染。
2025.8.28 将 get_posts_by_keyword 改造为了一个功能强大的新工具 get_content_by_keyword。这个新工具的核心功能是:根据用户提供的关键词,在所有授权访问的文章(包括私密和受密码保护的内容)中进行搜索。它不会返回整篇文章,而是以关键词为中心,提取并展示前后50个字符的文本片段。为了确保结果的有效性,所有返回的片段会首先按照其所在文章的匹配总数进行相关性排序,确保最相关的文章内容优先显示。同时,为了避免返回结果过长,该工具还内置了分页功能,将所有找到的文本片段进行分页,并在返回结果中提供清晰的摘要信息,如“从X篇文章中找到Y个匹配,当前显示第A页,共B页”,方便进行后续的翻页查询。此外,每个片段都包含了精确的字节偏移量(start),可以无缝配合 get_post_content_by_id 工具,直接从该位置读取文章的完整内容。
2025.8.27 彻底重构了,放弃了deep research的复杂多Agent方案,简简单单,单一LLM + MCP tools,ReAct架构。目前实现了九个工具。
内容检索工具: get_content_by_vector_search (语义搜索), get_posts_by_keyword (关键词搜索), get_posts_by_tag (标签浏览), get_posts_by_category (分类浏览)
内容导航工具: get_recent_posts (获取最新文章), get_site_stats (站点统计), get_post_content_by_id (完整文章阅读)
内容探索工具: list_all_tags (标签目录), list_all_categories (分类目录)
贵啊,Claude真贵啊!我今天一天往OpenRouter充了五六次,花了得有五六百块钱。
改进方向:1, 把摘要从粗暴截断改成LLM总结,2, get_posts_by_keyword返回含有该关键词的文段而非关键词所在文章的摘要
2025.7.15 bug记录:
- 向量库屏蔽没有做好,目前私密文章过滤掉了,但是密码保护的文章可以被向量库索引到,应该添加硬过滤。同时,如果一篇文章已经出现在向量库中,当公开状态变为私密或密码保护时,也应当执行从向量库清空的处理。等下,更好的逻辑应该是,向量库不过滤。只是索引的时候添加过滤选项,通过返回文段的meta信息过滤。这样,已登录用户还是可以享受到私密和密码保护文章的向量检索。
- 现在的迭代逻辑太简陋,下一步改进方向应该让第一步查询变成检索方案规划,然后调用多个LLM并发执行第一步规划的检索任务。最后再汇总。执行下一轮的“规划→执行”。最终一步汇总成最终答案回复用户。
- 但是这样一来上下文就太太太长了,所以在每一步执行结束之后应该再加一步内容压缩。因为检索回来的结果肯定没用的居多。内容压缩的逻辑应该是逐一判断每个文段是否与检索词相关,不相关就丢弃,对于相关的文段,也应当进一步提取这一段中具体哪几句话跟检索词相关,其他句子也删掉。由于是逐个文段判断,这一步也应该并发调用LLM,但应该手动限制并发数量,比如zhipu api最大并发数是30。同时用速度快的flash模型。
- 规划→并发执行→并发内容压缩→汇总→第二轮规划→并发执行→并发内容压缩→汇总→第三轮规划……
- 每一次“规划任务”的上下文应当包括每一次迭代的“汇总”信息。可以把这个整合步骤放在每一轮“汇总”阶段,直接跟上一轮的汇总文本合并。这时还应当加一步去重操作,这个不用调用LLM,因为向量库返回的文段是固定的,可以直接Python过滤掉。
- 另外我发现,几乎很少会用到关键词匹配,关键词能查到的内容,向量查询也能。而且如果用原生wordpress的关键词查询只能返回整篇文章或摘要,不能具体到段落,跟向量查询融合的不好。非要用关键词匹配的话,需要在向量库api里加一个端点直接从向量库而不是Mysql数据库查询。这样可以规范返回的格式。
- 还有一个问题是需不需要专门写入中间信息到文件,目前感觉存在python变量就够用了。
{
"plan_reasoning": "当前信息已确认博主与'宋同学'有关,但关系细节不足。下一步需深入挖掘'宋同学'的具体事迹并探索博主的其他社交圈。",
"tasks": [
{
"task_id": "task_001",
"query": "宋同学",
"tool": "search_posts_by_content",
"description": "精确查找所有提及'宋同学'的段落。"
},
{
"task_id": "task_002",
"query": "博主的朋友圈、同事、家人",
"tool": "semantic_vector_search",
"description": "模糊搜索与博主社交网络相关的文章。"
},
{
"task_id": "task_003",
"query": "聚会 OR 合作 OR 交流",
"tool": "search_posts_by_content",
"description": "关键词搜索描述社交活动场景的段落。"
}
]
}
用户的原始问题是:'博主的社会关系'
当前正在处理的任务是:'查找博主提到的朋友姓名'
--- 文档内容开始 ---
{单个文档片段的文本}
--- 文档内容结束 ---
请判断以上文档内容是否与当前任务'查找博主提到的朋友姓名'直接相关?
请只回答 "相关" 或 "不相关"。
用户的问题是:'博主的社会关系'
通过搜索,我收集到了以下高度相关的信息片段:
[
"从《与宋同学的毕业告别谈话》中提取的片段...",
"从《三观》中提取的片段...",
"从另一篇文章中提取的片段..."
]
请基于以上信息,生成一个全面、准确的回答。