Skip to content

sharmt1411/openai-skill-proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Skill API

中文 | English

基于 Claude Agent Skills 设计理念实现的 OpenAI 格式 API 服务,支持 Skill 元工具。

特性

  • Skill 元工具:LLM 自主决定何时调用技能
  • 📁 自动发现:扫描 skills/ 目录加载 .md 格式的技能文件
  • 🔄 两种注入策略
    • message 系统提示词替换({{SKILL}} 占位符)
    • system 消息注入(可清除/切换)
  • 🔌 OpenAI 兼容:标准 /v1/chat/completions 接口

项目架构

openai-skill-proxy/
├── src/                          # 源代码目录
│   ├── __init__.py
│   ├── main.py                   # FastAPI 主服务入口
│   ├── config.py                 # 配置管理(环境变量)
│   ├── models.py                 # Pydantic 数据模型
│   ├── skill_loader.py           # Skill 加载器(扫描 & 解析 .md 文件)
│   ├── skill_marker.py           # Skill 标记处理(提取/附加/移除)
│   ├── openai_client.py          # OpenAI API 客户端
│   ├── strategy_system.py        # 策略1: 系统提示词替换
│   └── strategy_message.py       # 策略2: 消息注入
│
├── skills/                       # 技能定义目录
│   ├── 发票.md                   # 发票相关问答知识库
│   ├── 融资.md                   # 融资相关问答知识库
│   ├── translator.md             # 翻译助手技能
│   ├── summarizer.md             # 摘要专家技能
│   └── code-review.md            # 代码审查技能
│
├── prompts/                      # 提示词模板目录
│   └── system_prompt.md          # 多轮对话系统提示词
│
├── tests/                        # 测试脚本目录
│   ├── test_skill.py             # Skill 加载测试(交互式)
│   ├── run_test.py               # 非交互式测试
│   └── chat_test.py              # 多轮对话测试
│
├── .env                          # 环境变量配置
├── requirements.txt              # Python 依赖
└── README.md                     # 项目说明

核心流程

整体架构图

┌─────────────────────────────────────────────────────────────────────┐
│                           用户请求                                   │
│                    POST /v1/chat/completions                        │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                          FastAPI 服务                               │
│                          (main.py)                                  │
├─────────────────────────────────────────────────────────────────────┤
│  1. 解析请求参数                                                     │
│  2. 注入 Skill 工具定义到 tools 列表                                 │
│  3. 调用上游 LLM API                                                │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                         ┌──────────┴──────────┐
                         ▼                      ▼
              ┌──────────────────┐    ┌──────────────────┐
              │  LLM 未调用 Skill │    │  LLM 调用 Skill   │
              │    直接返回响应   │    │  function_call   │
              └──────────────────┘    └──────────────────┘
                                              │
                                              ▼
                              ┌───────────────────────────────┐
                              │      处理 Skill 工具调用       │
                              │   handle_skill_tool_call()    │
                              ├───────────────────────────────┤
                              │  1. 解析 skill 名称            │
                              │  2. 从 skill_loader 获取内容   │
                              │  3. 应用注入策略               │
                              └───────────────────────────────┘
                                              │
                         ┌────────────────────┴────────────────────┐
                         ▼                                          ▼
              ┌──────────────────────┐              ┌──────────────────────┐
              │   策略1: system       │              │   策略2: message      │
              │   替换 {{SKILL}}      │              │   注入 user 消息       │
              │   占位符             │              │   <skill> 标签包裹     │
              └──────────────────────┘              └──────────────────────┘
                         │                                          │
                         └────────────────────┬────────────────────┘
                                              ▼
                              ┌───────────────────────────────┐
                              │    第二次调用 LLM API          │
                              │    (带 skill 上下文)           │
                              └───────────────────────────────┘
                                              │
                                              ▼
                              ┌───────────────────────────────┐
                              │       返回最终响应             │
                              └───────────────────────────────┘

Skill 工具调用流程

用户: "银行融资怎么操作?"
           │
           ▼
┌─────────────────────────────────────┐
│  第一次 LLM 调用                     │
│  (包含 Skill 工具定义)               │
└─────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────┐
│  LLM 返回 tool_calls:               │
│  {                                  │
│    "name": "Skill",                 │
│    "arguments": {"command": "融资"} │
│  }                                  │
└─────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────┐
│  加载 skills/融资.md 内容            │
│  注入到消息列表                      │
└─────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────┐
│  第二次 LLM 调用                     │
│  (带知识库上下文)                    │
└─────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────┐
│  LLM 基于知识库回答:                 │
│  "工商银行融资操作流程如下..."        │
└─────────────────────────────────────┘

快速开始

1. 安装依赖

pip install -r requirements.txt

2. 配置环境变量

cp .env.example .env
# 编辑 .env 填写 API 配置

.env 配置项

OPENAI_API_KEY=your-api-key
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4
SKILL_STRATEGY=message                    # system 或 message
SKILLS_DIR=skills
SKILL_SECOND_CALL_THINKING=false          # 二次请求是否开启思考
配置项 说明
SKILL_STRATEGY 技能注入策略:system(替换占位符) 或 message(消息注入)
SKILL_SECOND_CALL_THINKING 附加 Skill 后的二次请求是否开启思考模式:true=保持, false=关闭

关于二次思考:当检测到 Skill 调用时,系统会注入技能内容并发起二次请求。
设置为 false 可以避免二次请求再次执行思考过程,加快响应速度。

3. 启动服务

python -m src.main

服务将在 http://localhost:8000 启动。

4. 多轮对话测试

python tests/chat_test.py

API 接口

聊天完成

POST /v1/chat/completions

请求示例

{
  "model": "gpt-4",
  "messages": [{ "role": "user", "content": "帮我审查这段代码: print(hello)" }],
  "strategy": "message"
}

当 LLM 判断需要使用技能时,会自动调用 Skill 工具,系统将注入对应技能的提示词。

扩展参数

参数 类型 说明
strategy system | message 注入策略(默认从env环境变量读取)
include_skill_tool boolean 是否包含 Skill 工具(默认启用,设置 false 关闭)
stream boolean 是否启用流式输出(默认 false
top_p float 采样参数(直接透传上游)
temperature float 温度参数(直接透传上游)
max_tokens integer 最大 token 数(直接透传上游)

流式请求

支持 SSE 流式输出,设置 stream: true 即可:

{
  "model": "gpt-4",
  "messages": [{ "role": "user", "content": "你好" }],
  "stream": true
}

流式 Skill 调用处理

当流式回复中检测到 Skill 工具调用时:

  1. 已输出的内容(思考/文本)继续转发,不断开连接
  2. 系统注入 Skill 内容后发起二次请求
  3. 二次请求的响应追加到当前流中
用户请求 → 流式输出(思考内容)... → 检测到Skill调用 → 注入Skill → 二次请求 → 追加输出回复 → [DONE]

如果首个工具调用不是 Skill,则直接透传所有内容,不做拦截。

技能管理

GET  /v1/skills/list    # 列出所有技能
GET  /v1/skills/reload  # 重新加载技能
POST /v1/skills/clear   # 清除消息中的技能注入

创建技能

skills/ 目录创建 .md 文件:

---
name: my-skill
description: 技能描述,用于 LLM 决策是否调用
---

你是一个专业的 XXX...

## 工作流程

...

Skill 文件格式要求

  • 必须包含 YAML frontmatter
  • name: 技能名称(用于 LLM 调用)
  • description: 技能描述(LLM 根据此判断何时调用)
  • 正文内容:技能的知识库或指令

两种注入策略

策略对比

策略 触发条件 消息链结构 特点
system system 消息含 {{SKILL}} 替换占位符 + tool_calls 链 适合固定模板,自动清除历史
message 默认策略 tool_calls 链 + user 消息 灵活,支持清除/切换

方案一:系统提示词替换 (strategy: system)

在 system 消息中使用 {{SKILL}} 占位符,技能内容会替换此占位符:

请求示例

{
  "messages": [
    {
      "role": "system",
      "content": "你是助手。\n\n{{SKILL}}<--提示词必须要包含Skill占位符,用于后续替换。"
    },
    { "role": "user", "content": "工行融资怎么操作?" }
  ],
  "strategy": "system"
}

消息链流程

原始消息:
├── system: "你是助手。\n\n{{SKILL}}"
└── user: "工行融资怎么操作?"
        │
        ▼ (第一次 LLM 调用,返回 tool_calls)
        │
应用 skill 后:
├── system: "你是助手。\n\n[SKILL_CONTENT][融资.md 内容...][/SKILL_CONTENT]"  ← 用标记包裹
├── user: "工行融资怎么操作?"
├── assistant: { tool_calls: [{ name: "Skill", arguments: { command: "融资" } }] }
└── tool: "The '融资' skill has been loaded into system prompt."
        │
        ▼ (第二次 LLM 调用)
        │
最终回复: "工商银行融资操作流程如下..."

多轮对话处理

在 system 模式下进行多轮对话时,如果用户切换到不同的技能话题,系统会:

  1. 自动清除历史消息中之前的 Skill 工具调用记录(assistanttool_calls 和对应的 tool 消息)
  2. 还原占位符:将 [SKILL_CONTENT]旧内容[/SKILL_CONTENT] 还原为 {{SKILL}}
  3. 替换新技能:用新技能内容替换占位符
多轮对话示例:
├── system: "你是助手。\n\n[SKILL_CONTENT][融资.md 内容...][/SKILL_CONTENT]"
├── user: "工行融资怎么操作?"
├── assistant: { tool_calls: [...] }  ← 第一次 Skill 调用(会被清除)
├── tool: "..."                        ← 对应的 tool 消息(会被清除)
├── assistant: "融资操作流程..."
├── user: "发票问题怎么处理?"
        │
        ▼ (用户问了新话题,LLM 决定调用发票技能后,messages 修改为)
        │
处理后:
├── system: "你是助手。\n\n[SKILL_CONTENT][发票.md 内容...][/SKILL_CONTENT]"  ← 替换为新技能
├── user: "工行融资怎么操作?"
├── assistant: "融资操作流程..."
├── user: "发票问题怎么处理?"
├── assistant: { tool_calls: [{ name: "Skill", arguments: { command: "发票" } }] }
└── tool: "The '发票' skill has been loaded into system prompt."

方案二:消息注入 (strategy: message,推荐)

技能内容作为新的 user 消息注入,支持清除和切换:

请求示例

{
  "messages": [{ "role": "user", "content": "工行融资怎么操作?" }],
  "strategy": "message"
}

消息链流程

原始消息:
└── user: "工行融资怎么操作?"
        │
        ▼ (第一次 LLM 调用,返回 tool_calls)
        │
应用 skill 后:
├── user: "工行融资怎么操作?"
├── assistant: { tool_calls: [{ name: "Skill", arguments: { command: "融资" } }] }
├── tool: "The '融资' skill is loading"
└── user: "[SKILL_INJECTION:融资]\n[融资.md 内容...]\n[/SKILL_INJECTION]\n请根据上述技能知识库的内容回答用户的问题。"
        │
        ▼ (第二次 LLM 调用)
        │
最终回复: "工商银行融资操作流程如下..."

清除已注入的技能

POST /v1/skills/clear
{"messages": [...]}

Skill 标记与上下文恢复

为了让标准 OpenAI 客户端(不处理 _skill_context)也能在多轮对话中保持 Skill 上下文,服务端会在首次加载 Skill 时在回复末尾附加一个不可见的标记

<!-- @skill:技能名称 -->

工作原理

【首次触发 Skill】
用户: "融资怎么操作?"
     ↓
LLM 调用 Skill("融资") → 注入知识库 → 生成回复
     ↓
服务端返回assistant: "融资操作流程如下...<!-- @skill:融资 -->"
     ↓
客户端保存 assistant 消息(包含标记)


【后续追问】
用户: "手续费多少?"
     ↓
客户端发送 messages(包含带标记的历史 assistant 消息)
     ↓
服务端检测到 <!-- @skill:融资 --> 标记
     ↓
自动恢复 Skill 上下文(重建 tool_call 链 + 注入知识库)
     ↓
LLM 直接回答(无需再次调用 Skill 工具)
     ↓
返回回复(不再附加新标记)

恢复机制

当服务端检测到历史消息中有 Skill 标记时,会自动:

  1. 重建 tool_call 链:生成随机 ID,插入完整的 assistant(tool_calls) + tool(result) 消息
  2. 注入 Skill 内容:根据策略注入到 system prompt 或 messages
  3. 插入位置:在首次带标记的 assistant 消息之前
  4. 清理标记:调用 LLM 前移除所有 assistant 消息中的 marker(LLM 不应看到这些标记)

标记规则

场景 是否附加标记
首次调用 Skill ✅ 附加
追问(预加载模式) ❌ 不附加
切换到新 Skill ✅ 附加新标记
无 Skill 相关对话 ❌ 不附加

注意:标记仅用于前端与服务端之间传递上下文,不会发送给 LLM 模型。


测试脚本

脚本 说明
tests/api_test.py 多轮对话测试(真实 API)
tests/stream_test.py 流式对话测试(真实 API)
tests/stream_chat_test.py 流式多轮对话(验证 Skill 标记)
tests/test_marker.py Skill 标记模块单元测试

不足

调用Skill工具只能调用一个无法并列调用。

License

MIT


About

OpenAI-compatible API proxy with Skill meta-tool - LLM autonomously loads knowledge when needed

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages