141.Agent-多Agent框架-编写并使用Skills

摘要 :本文基于 Tool Use(Function Calling)机制,实现了一个支持技能(Skill)按需加载的本地智能代理(Agent)。全文围绕「发现阶段」与「激活阶段」两条主线展开:发现阶段通过扫描 .agents/skills 目录下的 SKILL.md 文件,解析 YAML frontmatter 中的技能描述并组装为 Skill 对象;激活阶段则创建 Agent,将技能名称与描述注入系统提示词,并通过 load_skill 工具在对话中按需加载技能内容,实现渐进式技能调用。文章附带了 frontend-design、xlsx、docx 三个示例技能及完整可运行的 Python 代码。

内容参考于:图灵AI大模型全栈

发现阶段

发现阶段是读取所有SKILL.md文件中YAML的内容,如下图红框YAML部分的内容

如下图技能所在的目录,技能都放在.agents/skills目录里

Skill类型,如下图这个类型是我们自定义的,就是为了使用下图红框的三个成员变量

它的加载方式如下图,通过在.agents/skills目录中找所有的 SKILL.md 文件(位置2遍历目录里所有的内容和位置3判断当前的内容是不是SKILL.md),找到之后通过 _parse_description 得到YAML部分的内容,YAML内容在 --- 和 --- 之间,_parse_description里是通过正则表达式来找 --- 和 --- 之间的内容,对应下图位置5,找到之后,组装成Skill类型(位置6),然后放到一个字典类型的列表中(位置7),字典的key是SKILL.md的目录,值是Skill类型,也就是yaml中的name和description和content(Skill的全内容),然后遍历完所有的目录,就返回字典列表

激活阶段,在这一步会创建Agent

首先是大模型(Agent)的系统提示词

然后使用OpenAI创建模型

初始化技能加载类

获取技能索引列表

获取技能索引列表中的 名字(name)和技能描述(description),这一步也就是实现渐进式加载

然后把技能的 名字 和 技能的描述 放到系统提示词中

然后创建一个工具,这里是通过 Function_Calling 的方式创建工具,112节中有写 Function_Calling ,它最终是调用 load_skill 来加载技能和执行技能

然后是历史对话

到这初始化部分就完成了,接下来是提问,然后调用技能,如下图红框首先根据用户提出的问题创建一个消息,并添加到历史消息中

然后获取大模型的响应,向大模型提问时把历史消息放进去了,再加上系统提示词,大模型会根据问题返回是否加载技能

然后判断是否调用工具或直接获取答案,stop_reason的值是tool_calls说明要调用工具,它的值是stop不调用任何工具可以直接获取回答,它们是self.client.chat.completions.create方式使用OpenAI协议固定返回的两个值

如果调用工具的话,就构建一个大模型返回内容的消息,并添加到历史对话中,message.tool_calls里面是要调用的工具和工具的参数

message.tool_calls内容结构如下图

然后开始调用技能

当前实例实现的调用技能也就是把SKILL.md文件里的内容放到上下文中,也就是添加到历史对话中,下图位置1是从技能索引中根据名字查找对应的全部内容,位置2和位置3是构建一个内容,位置2的内容是SKILL.md里面的内容,位置3是找不到SKILL.md文件时的回复,然后在位置4构建一个工具的回复(回复的内容就是位置2或位置3构建的内容)

然后把工具回复的内容添加到历史对话中,这样就把技能加载到了上下文中,大模型也就可以使用技能了,如果当前的实例如果技能中需要调用脚本当前的代码没有办法实现,因为没有沙箱环境,现在只能使用不加载脚本的技能,后面会实现调用技能中的脚本

技能的内容

frontend-design技能

复制代码
---
name: frontend-design
description: 创建具有高设计质量的独特的生产级前端界面。
---
 
# Frontend Design Skill
 
当用户请求创建网页、前端组件或 UI 界面时,遵循以下原则:
 
## 核心原则
1. **视觉独特性**:避免通用 AI 风格,每个设计都应有独特的视觉个性。
2. **代码质量**:输出生产就绪的 HTML/CSS/JS 或 React 代码。
3. **响应式设计**:默认支持移动端和桌面端。
4. **无障碍访问**:遵循 WCAG 标准,确保可访问性。
 
## 技术规范
- HTML: 语义化标签,正确的 meta 标签
- CSS: CSS 变量管理主题,Flexbox/Grid 布局
- JS: 原生 JS 优先,必要时使用框架
- React: 函数组件 + Hooks,Tailwind CSS 样式
 
## 设计要求
- 选择有特色的字体(避免 Arial, Inter 等通用字体)
- 使用有主张的配色方案
- 加入适当的动画和微交互
- 注重空白和排版节奏

xlsx技能

复制代码
---
name: xlsx
description: 使用openpyxl或pandas创建和操作Excel电子表格。
---
 
# XLSX Skill
 
当用户需要创建或操作 Excel 文件时,使用 openpyxl(精细控制)或 pandas(数据处理)。
 
## 安装
```bash
pip install openpyxl pandas
```
 
## 使用 openpyxl 创建样式化表格
```python
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
 
wb = Workbook()
ws = wb.active
ws.title = "数据表"
 
# 写入标题行(加粗 + 背景色)
headers = ["姓名", "部门", "薪资"]
for col, header in enumerate(headers, 1):
    cell = ws.cell(row=1, column=col, value=header)
    cell.font = Font(bold=True, color="FFFFFF")
    cell.fill = PatternFill(fill_type="solid", fgColor="4472C4")
    cell.alignment = Alignment(horizontal="center")
 
wb.save("output.xlsx")
```
 
## 使用 pandas 处理数据
```python
import pandas as pd
 
df = pd.read_csv("data.csv")
df_summary = df.groupby("部门")["薪资"].mean()
df_summary.to_excel("summary.xlsx", index=True)
```

docx技能

复制代码
---
name: docx
description: 使用专业格式创建和操作Word文档(.docx文件)。
---
 
# DOCX Skill
 
当用户需要创建或操作 Word 文档时,使用 python-docx 库。
 
## 安装
```bash
pip install python-docx
```
 
## 核心用法
 
### 创建文档

```python
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
 
doc = Document()
 
# 添加标题
doc.add_heading('文档标题', level=0)
 
# 添加段落
p = doc.add_paragraph('正文内容')
p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
 
# 设置字体
run = p.runs[0]
run.font.size = Pt(12)
run.font.name = '宋体'
 
# 保存
doc.save('output.docx')
```
 
## 最佳实践
- 始终使用样式系统,不要手动设置每处格式
- 表格数据使用 add_table() 而非手动排版
- 图片使用 add_picture() 并指定宽度
- 输出文件保存到 /file/outputs/

效果图:

完整代码:

python 复制代码
# os:操作系统接口,用于读取环境变量
import os
# re:正则表达式模块,用于解析YAML frontmatter
import re
# json:JSON处理模块,用于解析工具调用参数
import json
# pathlib:面向对象的文件系统路径库
from pathlib import Path
# OpenAI:阿里云DashScope兼容的OpenAI客户端
from openai import OpenAI
# dotenv:从.env文件加载环境变量
from dotenv import load_dotenv

# ============================================================
# 配置部分
# ============================================================
# 从 .env 文件加载环境变量
load_dotenv()

# API配置
# DASHSCOPE_API_KEY:阿里云DashScope API密钥,用于身份验证
API_KEY = os.getenv("DASHSCOPE_API_KEY")
# DASHSCOPE_BASE_URL:API基础URL,如果使用自定义端点需要设置
BASE_URL = os.getenv("DASHSCOPE_BASE_URL")

# 技能目录路径
# 所有技能都存储在 .agents/skills 目录下,每个技能一个子目录
root_dir = Path(__file__).parent.parent
SKILLS_DIR = Path(f"{root_dir}/skills/.agents/skills")

# 使用的模型名称
# 这里使用阿里云DashScope的qwen3.5-plus模型
MODEL = "qwen3.7-flash"

# ============================================================
# 1. Skill 数据结构
# ============================================================

class Skill:
    """
    技能单元类,表示一个可调用的专业技能。

    每个技能对应一个 SKILL.md 文件,包含:
    - 技能名称(从目录名获取)
    - 技能描述(从 YAML frontmatter 解析)
    - 技能内容(SKILL.md 文件的完整内容)

    属性:
        name (str): 技能目录名称,例如 "frontend-design"
        description (str): 技能描述,用于工具描述,帮助模型理解何时使用该技能
        content (str): SKILL.md 文件的完整内容,作为工具调用的结果返回给模型
    """

    def __init__(self, name: str, description: str, content: str):
        """
        初始化技能单元。

        参数:
            name (str): 技能目录名称,例如 "frontend-design"
            description (str): 技能描述,从 YAML frontmatter 解析
            content (str): SKILL.md 文件的完整内容
        """
        self.name = name               # 技能目录名,如 "frontend-design"
        self.description = description  # 从 frontmatter 解析的描述,用于 Tool description
        self.content = content         # SKILL.md 完整内容,作为 tool_result 注入给模型
    # print(Skill),这时会打印Skill类里的 __repr__ 函数的返回值
    def __repr__(self):
        """
        返回技能的字符串表示形式。

        返回:
            str: 技能的可读表示,例如 "Skill(name='frontend-design')"
        """
        return f"Skill(name={self.name!r})"

# ============================================================
# 2. Skill 加载器,发现阶段
# ============================================================

class SkillLoader:
    """
    技能加载器类,负责从文件系统加载所有技能。

    该类会扫描指定目录下的所有子目录,每个包含 SKILL.md 文件的子目录
    被视为一个独立的技能。加载器会解析每个技能的元数据(描述)和内容,
    并返回一个以工具名称为键的字典,便于快速查找。

    属性:
        skills_dir (Path): 技能目录的路径
    """

    def __init__(self, skills_dir: Path):
        """
        初始化技能加载器。

        参数:
            skills_dir (Path): 技能目录的路径,例如 Path("./.agents/skills")
        """
        self.skills_dir = skills_dir

    def load_all(self) -> dict[str, Skill]:
        """
        加载所有技能并返回技能字典。

        扫描 skills_dir 下的所有子目录,每个含有 SKILL.md 的子目录
        被视为一个 Skill。返回以 tool_name 为键的字典,便于 tool_use 时快速查找。

        返回:
            dict[str, Skill]: 技能字典,键为工具名称,值为 Skill 对象

        示例:
            >>> loader = SkillLoader(Path("./.agents/skills"))
            >>> skills = loader.load_all()
            >>> print(list(skills.keys()))
            ['skill_docx', 'skill_frontend_design', 'skill_xlsx']
        """
        skills: dict[str, Skill] = {}

        # 检查技能目录是否存在
        if not self.skills_dir.exists():
            print(f"技能目录不存在: {self.skills_dir}")
            return skills

        # 遍历技能目录下的所有子目录
        for skill_dir in sorted(self.skills_dir.iterdir()):
            # 跳过非目录文件
            if not skill_dir.is_dir():
                continue

            # 检查 SKILL.md 文件是否存在
            skill_md = skill_dir / "SKILL.md"
            if not skill_md.exists():
                continue

            # 读取 SKILL.md 文件内容
            content = skill_md.read_text(encoding="utf-8")

            # 从 YAML frontmatter 中解析技能描述
            description = self._parse_description(content)

            # 创建 Skill 对象
            skill = Skill(name=skill_dir.name, description=description, content=content)

            # 以 skill name 为键存储,便于 load_skill 时快速查找
            skills[skill.name] = skill
            print(f"加载: [{skill.name}]  →  description={skill.description[:30]}...")

        print(f"\n共加载 {len(skills)} 个 Skill\n")
        return skills

    @staticmethod
    def _parse_description(content: str) -> str:
        """
        从 SKILL.md 的 YAML frontmatter 中提取 description 字段。

        YAML frontmatter 是文件开头用 --- 包围的 YAML 块,
        包含技能的元数据,如名称、描述、版本等。

        参数:
            content (str): SKILL.md 文件的完整内容

        返回:
            str: 技能描述,如果解析失败则返回 "通用技能"

        示例:
            >>> content = '''---
            ... name: frontend-design
            ... description: 创建具有高设计质量的独特的生产级前端界面。
            ... ---
            ... # 技能指南
            ... '''
            >>> SkillLoader._parse_description(content)
            '创建具有高设计质量的独特的生产级前端界面。'
        """
        # 使用正则表达式匹配 YAML frontmatter 块
        # ^---\s*\n 匹配开头的 ---
        # (.*?)\n--- 匹配中间的内容直到下一个 ---
        # re.DOTALL 使 . 也匹配换行符
        m = re.match(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
        if not m:
            return "通用技能"

        # 提取 YAML frontmatter 内容
        fm = m.group(1)

        # 从 YAML 中提取 description 字段
        # description:\s*["\']?(.+?)["\']?\s*$ 匹配 description: "value" 或 description: value
        dm = re.search(r'description:\s*["\']?(.+?)["\']?\s*$', fm, re.MULTILINE)

        # 返回描述,去除引号和首尾空格
        return dm.group(1).strip().strip('"\'') if dm else "通用技能"

# ============================================================
# 3. 创建Agent,激活阶段
# ============================================================

class LocalAgent:
    """
    基于 Tool Use 的智能代理类(opencode 风格)。

    这是整个系统的核心类,实现了基于工具调用的智能对话代理。
    它能够根据用户输入自动选择合适的技能(Skill),
    并结合技能指南生成专业的回答。

    属性:
        client (OpenAI): OpenAI 客户端实例
        skills (dict[str, Skill]): 技能字典,键为技能名称
        tool_definitions (list[dict]): 工具定义列表,用于 API 调用
        history (list[dict]): 对话历史记录

    工作机制(模拟 opencode 的 skills 机制):
      1. 在系统提示词中列出所有可用的 skills 及其描述。
      2. 模型根据用户请求的语义判断需要哪个 skill。
      3. 模型调用 load_skill 工具,指定 skill 名称。
      4. Agent 收到工具调用后,读取对应 SKILL.md 内容返回给模型。
      5. 模型结合技能指南,生成最终专业回答。
    """

    # 系统提示词模板,{skills_list} 会被替换为实际的技能列表
    SYSTEM_PROMPT_TEMPLATE = """你是一个智能助手,拥有一组专业技能工具。

## 可用技能列表

{skills_list}

## 使用规则

- 当用户的请求涉及某个专业领域时,必须先调用 load_skill 工具加载对应的技能指南,然后再作答。
- 调用 load_skill 时,name 参数必须是上面列出的技能名称之一(如 "docx"、"frontend-design"、"xlsx")。
- 获得技能指南后,严格遵循指南中的规范、最佳实践和代码风格。
- 如果任务不需要特定专业技能(如闲聊、一般知识问答),直接回答即可,无需调用工具。
- 一次请求可以调用多次 load_skill(如同时需要前端 + 文档技能)。
"""

    def __init__(self, api_key: str, base_url: str, skills_dir: Path):
        """
        初始化智能代理。

        参数:
            api_key (str): API 密钥,用于身份验证
            base_url (str): API 基础 URL,如果使用自定义端点需要设置
            skills_dir (Path): 技能目录的路径

        初始化过程:
            1. 创建 OpenAI 客户端实例
            2. 加载所有技能
            3. 构建技能描述列表(用于系统提示词)
            4. 构建工具定义列表(只有一个 load_skill 工具)
            5. 初始化对话历史
        """
        # 创建 OpenAI 客户端,用于与 API 通信
        self.client = OpenAI(api_key=api_key, base_url=base_url)

        # 加载所有 Skills,key = skill_name(如 "docx"、"frontend-design")
        loader = SkillLoader(skills_dir)
        self.skills: dict[str, Skill] = loader.load_all()

        # 构建技能描述列表,用于系统提示词   获取对应的name和description
        skills_list = self._build_skills_list()

        # 动态生成系统提示词
        self.system_prompt = self.SYSTEM_PROMPT_TEMPLATE.format(
            skills_list=skills_list
        )

        # 构建 Tool Definitions 列表(只有一个 load_skill 工具)
        self.tool_definitions: list[dict] = [self._create_load_skill_tool()]

        # 多轮对话历史,存储用户和助手的交互记录
        self.history: list[dict] = []

    # ----------------------------------------------------------
    # 公开接口
    # ----------------------------------------------------------

    def chat(self, user_input: str) -> str:
        """
        处理一轮用户输入,返回最终回答。

        这是代理的核心方法,实现了完整的对话流程:
        1. 将用户输入添加到对话历史
        2. 调用 API 获取模型响应
        3. 如果模型选择调用工具,处理工具调用并获取结果
        4. 将工具结果返回给模型,继续对话
        5. 重复步骤 2-4,直到模型生成最终回答

        参数:
            user_input (str): 用户输入的文本

        返回:
            str: 代理的最终回答

        示例:
            >>> agent = LocalAgent(api_key, base_url, skills_dir)
            >>> response = agent.chat("帮我创建一个React组件")
            >>> print(response)
            '好的,我将使用前端设计技能帮你创建一个React组件...'
        """
        # 将用户消息加入历史
        self.history.append({"role": "user", "content": user_input})

        # Agentic Loop:持续处理,直到模型返回 end_turn(无更多工具调用)
        while True:
            # 调用 API 获取模型响应
            response = self._call_api()
            message = response.choices[0].message
            stop_reason = response.choices[0].finish_reason

            print(f"  [Agent] stop_reason={stop_reason!r}")

            # 根据停止原因处理响应
            if stop_reason == "tool_calls":
                # 模型选择调用工具
                # 将本轮 assistant 消息加入历史
                self.history.append({
                    "role": "assistant",
                    "content": message.content or "",        # 可能为 None
                    "tool_calls": [
                        {
                            "id": tc.id,
                            "type": "function",
                            "function": {
                                "name": tc.function.name,
                                "arguments": tc.function.arguments  # JSON 字符串
                            }
                        }
                        for tc in (message.tool_calls or [])
                    ]
                })

                # 处理所有 tool_call,构建 tool 结果列表
                tool_results = self._handle_tool_calls(message.tool_calls)

                # 每个 tool result 单独作为一条 tool 消息追加
                for result in tool_results:
                    self.history.append({
                        "role": "tool",
                        "tool_call_id": result["tool_call_id"],  # 必须与请求中的 id 对应
                        "content": result["content"]             # 字符串
                    })
                # 继续循环,让模型处理工具结果

            elif stop_reason == "stop":
                # 模型完成回答,没有更多工具调用
                final_text = message.content or ""

                # 将最终回答加入历史
                self.history.append({
                    "role": "assistant",
                    "content": final_text
                })
                return final_text

            else:
                # length 等其他停止原因
                return message.content or f"[停止原因: {stop_reason}]"

    def reset(self):
        """
        清空对话历史,重新开始对话。

        调用此方法会清除所有之前的对话记录,
        代理将从全新的状态开始。
        """
        self.history = []
        print("对话历史已清空\n")

    def show_tools(self):
        """
        显示所有已注册的技能和工具。

        打印所有可用的技能列表和工具定义,
        帮助用户了解代理具备哪些能力。
        """
        print("可用技能列表:")
        for skill in self.skills.values():
            print(f"  - {skill.name}: {skill.description}")
        print()

        print("已注册的工具:")
        for td in self.tool_definitions:
            print(f"  - {td['function']['name']}: {td['function']['description'][:50]}...")
        print()

    # ----------------------------------------------------------
    # 内部方法
    # ----------------------------------------------------------

    def _build_skills_list(self) -> str:
        """
        构建技能描述列表,用于系统提示词。

        将所有可用的技能以 Markdown 列表格式展示,
        让模型知道有哪些技能可用以及每个技能的用途。

        返回:
            str: 技能描述列表的 Markdown 文本

        示例返回值:
            - **docx**: 使用专业格式创建和操作Word文档(.docx文件)
            - **frontend-design**: 创建具有高设计质量的独特的生产级前端界面
            - **xlsx**: 使用openpyxl或pandas创建和操作Excel电子表格
        """
        lines = []
        for skill in self.skills.values():
            lines.append(f"- **{skill.name}**: {skill.description}")
        return "\n".join(lines)

    def _create_load_skill_tool(self) -> dict:
        """
        创建 load_skill 工具定义。

        这是唯一的工具,用于按需加载指定 skill 的完整内容。
        模型通过调用此工具来获取技能指南。

        返回:
            dict: OpenAI 兼容的工具定义字典
        """
        return {
            "type": "function",
            "function": {
                "name": "load_skill",
                "description": (
                    "加载指定技能的完整指南。当用户的任务涉及某个专业领域时,"
                    "调用此工具加载对应的技能指南,然后严格按照指南中的规范完成任务。"
                ),
                "parameters": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": (
                                "要加载的技能名称,必须是以下之一:"
                                + "、".join(self.skills.keys())
                            )
                        }
                    },
                    "required": ["name"]
                }
            }
        }

    def _call_api(self):
        """
        调用模型 API,发送对话历史和工具定义。

        构建完整的请求消息,包括:
        1. 系统提示词(定义代理行为)
        2. 对话历史(用户和助手的交互)
        3. 工具定义(所有可用的技能工具)

        返回:
            API 响应对象,包含模型的回复和可能的工具调用
        """
        # 构建完整的消息列表
        messages = [
            {
                "role": "system",
                "content": self.system_prompt  # 使用动态生成的系统提示词
            },
            *self.history
        ]

        # 调用 API
        return self.client.chat.completions.create(
            model=MODEL,                    # 使用的模型
            # max_tokens=4096,                # 最大生成 token 数
            tools=self.tool_definitions,   # ← 所有 Skill 作为 Tools bind 到模型
            messages=messages,              # 对话历史
        )

    def _handle_tool_calls(self, tool_calls) -> list[dict]:
        """
        处理模型返回的工具调用请求。

        遍历所有工具调用请求,处理 load_skill 工具调用,
        返回对应的 SKILL.md 内容作为 tool message。

        参数:
            tool_calls: 模型返回的工具调用列表

        返回:
            list[dict]: 符合 OpenAI API 规范的工具结果列表

        返回格式:
            [
                {
                    "role": "tool",
                    "tool_call_id": "<对应 tool_call 的 id>",
                    "content": "<SKILL.md 内容>"
                },
                ...
            ]
        """
        import json
        tool_results = []

        # 遍历所有工具调用请求
        for tc in (tool_calls or []):
            tool_name = tc.function.name
            # 解析工具参数(OpenAI 格式是 JSON 字符串)
            tool_input = json.loads(tc.function.arguments)

            # 处理 load_skill 工具调用
            if tool_name == "load_skill":
                skill_name = tool_input.get("name", "")
                print(f"  [Tool Call] 加载技能: {skill_name!r}")

                # 查找对应的技能
                skill = self.skills.get(skill_name)

                if skill:
                    # 构建技能指南内容
                    result_content = (
                        f"# 技能指南已加载:{skill.name}\n\n"
                        f"{skill.content}\n\n"
                        "---\n请严格按照以上指南完成任务。"
                    )
                    print(f"→ 注入 [{skill.name}],{len(skill.content)} 字符")
                else:
                    # 技能未找到,列出可用的技能
                    available = ", ".join(self.skills.keys())
                    result_content = (
                        f"错误:未找到名为 {skill_name!r} 的技能。\n\n"
                        f"可用的技能有:{available}"
                    )
                    print(f" → 未找到技能: {skill_name!r}")
            else:
                # 未知的工具调用
                result_content = f"错误:未知的工具 {tool_name!r}。请使用 load_skill 工具加载技能。"
                print(f"  [Tool Call] 未知工具: {tool_name!r}")

            # 构建工具结果
            tool_results.append({
                "role": "tool",                  # ← Anthropic 是 "type": "tool_result"
                "tool_call_id": tc.id,           # ← Anthropic 是 "tool_use_id": block.id
                "content": result_content,
            })

        return tool_results

    @staticmethod
    def _extract_text(content_blocks: list) -> str:
        """
        从 content block 列表中提取所有文本内容。

        这是一个辅助方法,用于从复杂的响应结构中提取纯文本。
        支持两种格式:
        1. 具有 text 属性的对象
        2. 包含 type="text" 的字典

        参数:
            content_blocks (list): 内容块列表

        返回:
            str: 提取的文本内容,用换行符连接
        """
        parts = []
        for block in content_blocks:
            # 处理对象格式
            if hasattr(block, "text"):
                parts.append(block.text)
            # 处理字典格式
            elif isinstance(block, dict) and block.get("type") == "text":
                parts.append(block["content"])
        return "\n".join(parts).strip()

# ============================================================
# 4. 主程序
# ============================================================

def main():
    """
    主程序入口函数。

    这个函数实现了交互式命令行界面,允许用户与智能代理进行对话。
    程序流程:
    1. 显示欢迎信息
    2. 初始化智能代理
    3. 显示可用的技能工具
    4. 检查 API 密钥配置
    5. 进入交互式对话循环

    支持的命令:
    - 'reset':清空对话历史,重新开始
    - 'quit':退出程序
    - Ctrl+C:中断程序

    使用方法:
        直接运行 python skills_agent.py 即可启动交互式界面
    """
    # 显示欢迎信息
    print("=" * 60)
    print("   Skills Agent  (Tool Use 版)")
    print("=" * 60)
    print()

    # 初始化智能代理
    # 使用配置文件中的 API 密钥、基础 URL 和技能目录
    agent = LocalAgent(api_key=API_KEY, base_url=BASE_URL, skills_dir=SKILLS_DIR)

    # 显示所有可用的技能工具
    agent.show_tools()

    # 检查 API 密钥是否已配置
    if API_KEY == "your-api-key-here":
        print("未设置 ANTHROPIC_API_KEY,仅展示 Tool Definition 结构\n")
        # 显示工具定义的 JSON 结构,便于调试
        print(json.dumps(agent.tool_definitions, ensure_ascii=False, indent=2))
        print("\n请设置后重新运行:  export ANTHROPIC_API_KEY=sk-ant-...")
        return

    # 显示使用说明
    print("输入 'reset' 清空历史,'quit' 退出\n")

    # 交互式对话循环
    while True:
        try:
            # 获取用户输入
            user_input = input("You: ").strip()
        except (KeyboardInterrupt, EOFError):
            # 处理 Ctrl+C 或 EOF 信号
            print("\nGoodbye!")
            break

        # 跳过空输入
        if not user_input:
            continue

        # 处理退出命令
        if user_input.lower() == "quit":
            print("Goodbye!")
            break

        # 处理重置命令
        if user_input.lower() == "reset":
            agent.reset()
            continue

        # 处理用户输入,获取代理回答
        print()
        answer = agent.chat(user_input)
        print(f"\nAssistant:\n{answer}\n")

# ============================================================
# 程序入口点
# ============================================================
# 当直接运行此脚本时,调用 main() 函数启动交互式界面
# 如果作为模块导入,则不会自动执行
if __name__ == "__main__":
    main()

相关推荐
昨日之日20061 小时前
Winxvideo:AI全能工具,智能修复老视频照片、清理噪音、录屏剪辑超方便
人工智能·音视频
YHL1 小时前
🚀 LangGraph 从入门到实战:构建有状态的 AI Agent 工作流
人工智能
匠测AI说1 小时前
AI for Testing 提效实战·执行自动化(一):别让AI凭空写脚本,让它在你的框架里写,产出才能直接合入
人工智能·测试
田里的水稻1 小时前
EI_模仿学习IL---工程链路
人工智能·深度学习·学习·机器学习·迁移学习
snakeshe10101 小时前
Python零基础核心进阶:四大容器+函数全网超全详解
人工智能
2603_969734501 小时前
采访录音噪音大怎么修复人声:降噪之后还要检查可听性
人工智能
回眸&啤酒鸭1 小时前
【回眸】SEO 检测师实战应用与价值落地指南
人工智能·seo
能源革命1 小时前
AI 日报(2026-10-09)
人工智能
霍格沃兹测试学院-小舟畅学1 小时前
AI 智能化测试:从测试用例生成到自动执行的工程实践
人工智能·测试用例