TaiXu-Admin V0.1.1 的更新重点不在“又接了一个模型”,而在应用系统真正跑起来后会遇到的工程问题:技能怎么加载,知识库怎么重构,历史记忆怎么管,RAG 和 Agent 出错时怎么兜底。这些能力看起来不如模型参数醒目,却直接决定一个 LLM 应用能不能稳定交付。
Skill 从代码能力变成可配置资产
这次发布实现了 skill 技能加载,并支持在 YAML 中配置全局 skill 路径和项目 skill 路径。这个变化很关键:在 LLM+Agent 系统里,skill 往往对应一组可复用动作,例如检索、文档解析、调用内部 API、生成报表、触发工单等。
如果 skill 只能硬编码在后端里,团队很快会遇到两个问题:
- 通用能力和项目定制能力混在一起,升级困难。
- 不同项目复用同一套 Agent 框架时,需要频繁改代码和重新发布。
把 skill 路径放进 YAML 后,工程组织方式会清晰很多:全局路径放公共技能,项目路径放业务定制技能。后端 Python 负责扫描、加载、注册;具体项目只需要调整配置。
可以这样实践一个简化版配置结构:
# config/skills.yaml
skills:
global_paths:
- ./skills/common
- ./skills/integrations
project_paths:
- ./projects/customer_service/skills
agent:
default_timeout_seconds: 30
fail_fast: false
对应的 Python 加载逻辑可以保持非常小,便于接入现有后端。下面示例是假设每个 skill 文件暴露一个 register() 函数,返回技能元数据和处理函数:
# load_skills.py
from pathlib import Path
import importlib.util
import yaml
def load_module(file_path: Path):
spec = importlib.util.spec_from_file_location(file_path.stem, file_path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Cannot load module: {file_path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def load_skills(config_file: str):
config = yaml.safe_load(Path(config_file).read_text(encoding="utf-8"))
paths = config["skills"].get("global_paths", []) + config["skills"].get("project_paths", [])
registry = {}
for skill_dir in paths:
for file_path in Path(skill_dir).glob("*.py"):
module = load_module(file_path)
if not hasattr(module, "register"):
continue
skill = module.register()
registry[skill["name"]] = skill
return registry
if __name__ == "__main__":
skills = load_skills("config/skills.yaml")
print("Loaded skills:", list(skills.keys()))
运行前需要安装依赖并准备目录:
pip install pyyaml
mkdir -p config skills/common projects/customer_service/skills
再放一个最小 skill:
# skills/common/echo.py
def run(text: str):
return {"result": text}
def register():
return {
"name": "echo",
"description": "Return input text for connectivity tests.",
"handler": run,
}
这不是 TaiXu-Admin 的原始接口声明,而是一个可以参考的工程化组织方式:用配置隔离路径,用注册函数隔离实现,用 registry 给 Agent 调度层消费。
知识库和知识图谱库需要“可重构”
更新日志里提到“增加知识库与知识图谱库的自定义重构”。这说明系统开始正视 RAG 的生命周期问题。
很多 RAG 项目初版只关心“把文档切块、向量化、检索出来”,但上线后会出现更麻烦的情况:
- 文档结构变了,旧 chunk 的粒度不合适。
- embedding 模型换了,旧向量需要重建。
- 业务希望按部门、项目、权限重新组织知识。
- 知识图谱实体抽取规则调整,旧关系需要重新生成。
“自定义重构”的价值在于让知识资产可以被重新加工,而不是一次性灌进去后靠补丁维持。对于企业内部系统,这尤其重要,因为知识库不是静态百科,它更像一个持续变化的工程索引。
可以把重构任务设计成显式命令,而不是藏在接口调用里:
# 示例命令:按项目重建知识库索引
python manage_kb.py rebuild \
--project customer_service \
--source ./data/customer_service_docs \
--chunk-size 800 \
--chunk-overlap 120 \
--target vector_store
一个简化的任务参数可以这样描述:
# config/kb_rebuild.yaml
project: customer_service
source_dir: ./data/customer_service_docs
rebuild:
target: vector_store
chunk_size: 800
chunk_overlap: 120
clean_before_rebuild: true
knowledge_graph:
enabled: true
entity_types:
- product
- policy
- issue
- department
这里的重点不是某个参数名字,而是把“重构”当成可审计、可重复的工程动作:谁触发、处理了哪些源文件、用了什么切块策略、生成了哪个版本的索引,都应该能查到。
历史记忆管理决定 Agent 是否越跑越乱
V0.1.1 更新了历史记忆管理。对 Agent 系统来说,memory 不是简单聊天记录。它可能包括用户偏好、任务状态、工具调用结果、阶段性结论、失败原因、外部系统返回值等。
如果不管理,历史记忆会带来三个典型风险:
- 上下文越塞越多,成本和延迟持续上升。
- 过期信息被 Agent 当成事实继续使用。
- 不同用户、不同项目、不同任务之间发生记忆污染。
一个更稳妥的做法是把记忆分层:短期上下文用于当前对话,任务记忆用于当前工作流,长期记忆必须经过摘要、过滤或人工确认后再沉淀。
可以这样设计一个最小记忆记录结构:
{
"memory_id": "mem_20250101_001",
"scope": "project",
"project_id": "customer_service",
"user_id": "u_123",
"type": "tool_result",
"content": "The refund policy document was rebuilt at version kb_v42.",
"created_at": "2025-01-01T10:00:00Z",
"expires_at": "2025-02-01T00:00:00Z"
}
这类结构能帮助系统判断:这条记忆属于谁、在哪个范围内有效、什么时候应该过期。Agent 的“聪明”不只来自模型,也来自这些边界清楚的数据结构。
RAG 和 Agent 的异常处理不是附属功能
更新日志单独提到 RAG、Agent 等异常处理,这个点很实际。LLM 应用链路长,任何一段都可能失败:模型超时、向量库无结果、工具调用异常、JSON 解析失败、权限不足、知识图谱查询返回空集。
异常处理不能只打印日志。更合理的方式是分层处理:
- 用户侧返回可理解的信息,而不是 Python traceback。
- 调度层记录失败阶段,便于重试或降级。
- 工具层保留原始错误,方便定位。
- RAG 无召回时允许走澄清问题、通用回答或拒答策略。
一个可改造的 FastAPI 示例:
# app.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class AskRequest(BaseModel):
question: str
project_id: str
class RagNoResult(Exception):
pass
def retrieve_context(project_id: str, question: str):
# Replace this with a real vector DB or knowledge graph query.
if "unknown" in question.lower():
raise RagNoResult("No relevant context found")
return ["Refund requests must include order ID and payment channel."]
def run_agent(question: str, contexts: list[str]):
# Replace this with a real LLM or Agent call.
return {
"answer": f"Based on retrieved context: {contexts[0]}",
"used_context_count": len(contexts),
}
@app.post("/ask")
def ask(req: AskRequest):
try:
contexts = retrieve_context(req.project_id, req.question)
return run_agent(req.question, contexts)
except RagNoResult as exc:
return {
"answer": "I could not find reliable project knowledge for this question. Please add more details or update the knowledge base.",
"fallback": "no_rag_context",
"detail": str(exc),
}
except TimeoutError as exc:
raise HTTPException(status_code=504, detail="The Agent request timed out") from exc
except Exception as exc:
raise HTTPException(status_code=500, detail="Agent execution failed") from exc
本地运行:
pip install fastapi uvicorn pydantic
uvicorn app:app --reload
测试请求:
curl -X POST http://127.0.0.1:8000/ask \
-H 'Content-Type: application/json' \
-d '{"project_id":"customer_service","question":"How should refund requests be handled?"}'
这个例子把 RAG 无结果和系统异常分开处理。前者是业务可预期状态,后者才是服务错误。真实系统里还可以继续加入 trace_id、重试次数、工具调用日志和模型响应原文采样。
采用建议:先把边界跑稳,再扩展能力
TaiXu-Admin V0.1.1 的几个更新方向都指向同一件事:LLM 应用不是一个 prompt 加一个模型,而是一套有配置、有知识资产、有记忆、有异常边界的应用系统。
落地时可以按这个清单推进:
- Skill:区分全局技能和项目技能,避免业务逻辑散落在 Agent 提示词里。
- 知识库:把重构流程做成可重复任务,记录版本和参数。
- 知识图谱:明确实体、关系和抽取规则的变更策略。
- 记忆:设置 scope、过期时间和隔离规则,避免记忆污染。
- 异常:把 RAG 无召回、工具失败、模型超时、解析失败分开处理。
风险也要讲清楚:skill 动态加载会带来权限和代码安全问题,知识库重构会消耗算力并影响检索一致性,历史记忆如果缺少治理可能泄露敏感信息。适合的做法是先在一个项目内收敛路径、权限、日志和回滚方式,再逐步推广到更多 Agent 场景。