Agent-10-04.配置驱动Agent与人格Skills

系列:00 索引 · 上一篇:03 MCP · 下一篇:05 FastAPI


1. 行业常见问题

现象 痛点
一个「万能 Agent」包打天下 边界模糊、易越权、难评测
改 Prompt 要改代码并发版 运营/领域专家无法参与
多产品线的角色差异大 复制粘贴多个 server.py
工具越来越多 未授权 Agent 也能调高风险 tool

OpenClaw、WorkBuddy 类产品直觉:每个角色独立人格、工具集、知识范围,用户在工作台切换。


2. 该技术如何解决

配置驱动:把「角色是谁、用什么模型、连哪些 MCP、读哪些 Skills、能做什么」从代码抽到 YAML/JSON + Markdown Skills

运行时只做:

  1. 加载配置
  2. 拼 system prompt(persona + skills 正文)
  3. 挂载允许的 tools/MCP
  4. 执行统一 Agent 循环

3. 核心原理

3.1 配置分层

内容
Persona role、tone、constraints(禁止投资建议等)
Model 模型名、temperature、base_url
Skills 操作手册 Markdown(检索策略、报告结构)
Tools / MCP 能力白名单
Permissions submit、ingest、delete 等布尔闸门

3.2 Skills 是什么

不是可执行插件,而是 给 LLM 的 SOP(标准作业程序):何时用哪个 tool、输出必须带哪些字段、如何处理歧义。

4. 典型实现与代码示例

配置驱动由 AgentConfig(Pydantic)+ prompt_builder.build_system_prompt_from_config + 各目录 agent.yaml 实现,统一经 AgentRuntime.from_yaml() 加载。

4.1 契约与 prompt 拼装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# agent/uni/contracts/agent_config.py
class PersonaConfig(BaseModel):
role: str # 写入 system prompt 首行「你是 …」
tone: str = "专业" # 口吻约束,影响 LLM 回复风格
constraints: list[str] = Field(default_factory=list) # 禁止项列表,拼入 system

class ModelConfig(BaseModel):
name: str = "gpt-4.1-mini" # run_tool_loop / LangChain 调用的 model 名
temperature: float = 0.2 # 结构化/工具任务宜低温度
base_url: str | None = None # 非空时 LLMClient 走兼容 OpenAI 的自定义端点
api_key_env: str = "OPENAI_API_KEY" # 从此环境变量读 API Key

class McpServerConfig(BaseModel):
name: str # Runtime 内 MCP 连接池键名
transport: str = "stdio" # stdio 或 http;决定 StdioMcpClient 启动方式
command: str | None = None # stdio:子进程 command(如 uv)
args: list[str] = Field(default_factory=list) # stdio:server.py 等参数
cwd: str | None = None # 子进程工作目录
url: str | None = None # http 传输时的 MCP 端点
env: dict[str, str] = Field(default_factory=dict) # 注入子进程(如 ARGO_MCP_MODE=mock)

class PermissionsConfig(BaseModel):
allow_delete: bool = False # 是否允许 destructive tool(代码层拦截)
allow_submit_job: bool = False # 是否允许 submit_template 等长任务 tool
allow_ingest: bool = False # 是否允许 ingest 写入知识库

class AgentConfig(BaseModel):
schema_version: str = "1.0" # YAML 契约版本
id: str # RunStore / API 路由中的 agent_id
display_name: str | None = None # Web UI 展示名;空则用 id
persona: PersonaConfig # → build_system_prompt_from_config
model: ModelConfig = Field(default_factory=ModelConfig)
skills: list[str] = Field(default_factory=list) # 相对 agent.yaml 的 Skill 路径
mcp_servers: list[McpServerConfig] = Field(default_factory=list) # 启动并注入 tool loop
permissions: PermissionsConfig = Field(default_factory=PermissionsConfig)

# agent/uni/runtime/prompt_builder.py
def build_system_prompt_from_config(config: AgentConfig, skill_paths: list[Path]) -> str:
lines = [f"你是 {config.persona.role}。", ...]
for path in skill_paths:
lines.append(f"\n## Skill: {path.name}\n{path.read_text(encoding='utf-8')}")
return "\n".join(lines)

4.2 Runtime 加载 YAML

1
2
3
4
5
6
# agent/uni/runtime/agent_runtime.py
@classmethod
def from_yaml(cls, yaml_path: Path, *, workspace: Path | None = None) -> AgentRuntime:
config = AgentConfig.model_validate(yaml.safe_load(yaml_path.read_text()))
skill_paths = [yaml_path.parent / s for s in config.skills]
return cls(config, skill_paths=skill_paths, workspace=workspace)

permissions(如 allow_submit_job)在 代码层 拦截 tool 调用,不能只写在 system prompt。

4.3 示例:agent/literature/agent.yaml

1
2
3
4
5
6
7
8
9
10
id: literature_researcher
persona:
role: 科研文献检索与摘要专员
skills:
- ../../skills/literature/search_strategy.md
mcp:
servers:
- name: rag-gene
command: uv
args: ["run", "python", "mcp/mcp_rag_gene/server.py"]

4.4 工程验收

1
2
uv run python -m agent.literature.cli chat --mock-llm "BRCA1 siRNA 转录本如何选择?"
uv run python -m agent.method_kb.cli chat --mock-llm "siRNA 设计 workflow 有哪些步骤?"

5. 替代方案与优缺点

方案 优点 缺点
YAML + 统一 Runtime 多角色低成本、可测试 需设计 Schema 版本
每角色一个 Python 类 类型清晰 重复、难运营
CrewAI 角色 YAML 声明式多 Agent 与自定义 MCP 集成要学其约定
纯 Prompt 模板仓库 简单 无权限、无 tool 绑定
Fine-tune 专用模型 口吻极稳 成本高、工具仍要接

6. 自检题

  1. Skills 与 MCP Tools 的职责边界是什么?
  2. 为何 allow_submit_job: false 不能只写在 system 里?
  3. 多角色共享同一 Runtime 时,如何避免配置串台?

7. 延伸阅读

-------------本文结束感谢您的阅读-------------