Logo
热心市民王先生

核心能力验证

技术研究 人工智能 AI Agent

OAuth 流程在 中实现: 这种设计允许: - 安全存储访问令牌(支持 keyring 或文件存储) - 令牌自动刷新机制 - 避免在配置文件中硬编码 API Key

API 调用分析

Kimi CLI 的 web_search 功能采用 Moonshot Search API 作为底层搜索服务。这是一个由 Moonshot AI 官方提供的搜索能力接口,而非直接调用 Google、Bing 等公共搜索引擎 API。

服务端点识别

主端点 URL:

POST https://api.kimi.com/coding/v1/search

该端点通过 src/kimi_cli/auth/platforms.py 中的平台配置定义:

def _kimi_code_base_url() -> str:
    if base_url := os.getenv("KIMI_CODE_BASE_URL"):
        return base_url
    return "https://api.kimi.com/coding/v1"

# 搜索服务 URL: f"{_kimi_code_base_url()}/search"

从这段代码可以看出,搜索服务端点遵循 Moonshot AI 的 Coding API 体系,这意味着该搜索服务是专门为 AI 编程助手场景优化的。

认证机制详解

Kimi CLI 实现了双层认证机制,支持 OAuth 2.0 和 API Key 两种方式:

1. OAuth 2.0 认证(推荐)

class OAuthRef(BaseModel):
    """Reference to OAuth credentials stored outside the config file."""
    storage: Literal["keyring", "file"] = "file"
    key: str

class MoonshotSearchConfig(BaseModel):
    base_url: str
    api_key: SecretStr
    oauth: OAuthRef | None = None

OAuth 流程在 src/kimi_cli/auth/oauth.py 中实现:

def resolve_api_key(self, api_key: SecretStr, oauth: OAuthRef | None) -> str:
    if oauth:
        token = self._access_tokens.get(oauth.key)
        if token is None:
            persisted = load_tokens(oauth)
            if persisted:
                self._cache_access_token(oauth, persisted)
                token = self._access_tokens.get(oauth.key)
        if token:
            return token
    return api_key.get_secret_value()

这种设计允许:

  • 安全存储访问令牌(支持 keyring 或文件存储)
  • 令牌自动刷新机制
  • 避免在配置文件中硬编码 API Key

2. API Key 认证

当 OAuth 未配置时,直接使用配置中的 API Key:

headers = {
    "Authorization": f"Bearer {api_key}",
}

请求构造分析

完整的 HTTP 请求构造如下(来自 src/kimi_cli/tools/web/search.py):

async with (
    new_client_session() as session,
    session.post(
        self._base_url,
        headers={
            "User-Agent": USER_AGENT,  # KimiCLI/{VERSION}
            "Authorization": f"Bearer {api_key}",
            "X-Msh-Tool-Call-Id": tool_call.id,
            **self._runtime.oauth.common_headers(),
            **self._custom_headers,
        },
        json={
            "text_query": params.query,
            "limit": params.limit,
            "enable_page_crawling": params.include_content,
            "timeout_seconds": 30,
        },
    ) as response,
):

请求头详解:

Header来源说明
User-Agentkimi_cli.constant.USER_AGENT标识客户端版本
AuthorizationBearer TokenOAuth 令牌或 API Key
X-Msh-Tool-Call-IdTool Call 上下文跟踪工具调用链路
X-Msh-PlatformOAuth Manager固定值 kimi_cli
X-Msh-VersionCLI 版本语义化版本号
X-Msh-Device-Name系统信息设备主机名
X-Msh-Device-Model系统信息OS 和架构
X-Msh-Os-Version系统信息操作系统版本
X-Msh-Device-Id系统信息唯一设备 ID

请求参数结构

class Params(BaseModel):
    query: str  # 搜索查询文本
    limit: int = 5  # 返回结果数量,范围 1-20,默认 5
    include_content: bool = False  # 是否抓取网页全文内容

参数说明:

  • text_query: 搜索关键词,直接透传用户输入
  • limit: 控制返回结果数量,最大 20 条。限制这个值是为了控制 Token 消耗
  • enable_page_crawling: 启用后会抓取每个结果页面的完整内容,会显著增加 Token 消耗,因此默认关闭
  • timeout_seconds: 固定 30 秒超时

响应数据结构验证

响应模型定义

class SearchResult(BaseModel):
    site_name: str      # 网站名称(如 Wikipedia、GitHub)
    title: str          # 页面标题
    url: str            # 页面 URL
    snippet: str        # 搜索结果摘要
    content: str = ""   # 页面完整内容(仅当 enable_page_crawling=True)
    date: str = ""      # 发布/更新日期
    icon: str = ""      # 网站 favicon URL
    mime: str = ""      # MIME 类型

class Response(BaseModel):
    search_results: list[SearchResult]

实际响应示例

虽然代码中没有展示真实响应示例,但从解析逻辑可以推断格式:

{
  "search_results": [
    {
      "site_name": "GitHub",
      "title": "MoonshotAI/kimi-cli - Kimi Code CLI",
      "url": "https://github.com/MoonshotAI/kimi-cli",
      "snippet": "Kimi Code CLI is your next CLI agent...",
      "content": "",
      "date": "2026-02-05",
      "icon": "https://github.com/favicon.ico",
      "mime": "text/html"
    }
  ]
}

结果格式化输出

工具将搜索结果转换为 Markdown 格式供 AI 消费:

for i, result in enumerate(results):
    if i > 0:
        builder.write("---\n\n")
    builder.write(
        f"Title: {result.title}\nDate: {result.date}\n"
        f"URL: {result.url}\nSummary: {result.snippet}\n\n"
    )
    if result.content:
        builder.write(f"{result.content}\n\n")

输出格式:

Title: {title}
Date: {date}
URL: {url}
Summary: {snippet}

{content if available}

---

Title: {title}
...

技术缺口分析

什么是”缺口”

在此场景下,“缺口”指如果想要复用该方案自建搜索功能时面临的技术障碍。

已识别的关键缺口

1. 私有 API 访问权限缺口

缺口描述: Moonshot Search API (api.kimi.com/coding/v1/search) 是一个私有端点,仅对 Kimi CLI 官方应用开放。从代码中可以看到,该服务需要配置在平台定义中:

Platform(
    id=KIMI_CODE_PLATFORM_ID,
    name="Kimi Code",
    base_url=_kimi_code_base_url(),
    search_url=f"{_kimi_code_base_url()}/search",  # 私有端点
    fetch_url=f"{_kimi_code_base_url()}/fetch",
)

影响: 第三方开发者无法直接调用该端点,需要:

  • 使用 Moonshot AI 官方提供的开放平台 API(moonshot.cn 或 moonshot.ai)
  • 但这些开放平台的端点定义中不包含 search_url(从代码可见,只有 kimi-code 平台有 search_url)

2. OAuth 认证体系缺口

缺口描述: Kimi CLI 使用完整的 OAuth 2.0 流程进行认证,包括:

  • 授权码流程
  • 令牌刷新
  • 设备绑定

影响: 自建搜索功能需要自行实现:

  • OAuth 客户端注册
  • 登录流程 UI
  • 令牌管理
  • 或者退回到简单的 API Key 方案

3. 工具框架依赖缺口

缺口描述: SearchWeb 工具深度集成在 Kosong 工具框架中:

class SearchWeb(CallableTool2[Params]):
    name: str = "SearchWeb"
    params: type[Params] = Params

依赖项包括:

  • kosong.tooling 框架
  • kimi_cli.soul.agent.Runtime
  • kimi_cli.soul.toolset 工具集管理

影响: 无法直接复制粘贴代码使用,需要:

  • 理解 Kosong 框架的 Tool 抽象
  • 或自行实现适配层

4. 配置管理缺口

缺口描述: 搜索配置与整体 CLI 配置深度耦合:

class Services(BaseModel):
    moonshot_search: MoonshotSearchConfig | None = None

配置来源包括:

  • 登录流程动态写入
  • 环境变量
  • 配置文件

影响: 自建方案需要重新设计配置管理体系。

可行性验证结论

验证方法

通过对源代码的静态分析,我们得出以下结论:

可行性等级: 需要变通方案

维度评估说明
API 访问不可直接复用Moonshot Search API 是私有接口
架构设计高度可参考工具化、配置化设计模式优秀
代码复用部分可行需要适配到自建框架
认证方案需重新设计OAuth 体系可借鉴但需自行实现

建议路径

Plan A: 使用 Moonshot AI 开放平台标准 API(如果未来开放搜索能力) Plan B: 采用第三方搜索 API(Google Custom Search、Bing Search API 等)并借鉴 Kimi CLI 的架构设计

参考资料