CoPaw Agent 对接 Python 客户端开发指南:实现流式响应与实时打印

CoPaw Agent Python 客户端开发指南:实现流式响应与实时打印

前言

在 AI Agent 应用开发中,如何与本地部署的 AI 服务进行高效通信是一个常见需求。本文将介绍如何使用 Python 实现一个 CoPaw Agent 客户端,支持流式响应(Streaming)实时打印功能,让你能够像使用 ChatGPT 一样获得流畅的对话体验。

什么是 CoPaw?

CoPaw 是一个本地部署的多智能体协作系统。

通过本文的 Python 客户端,你可以:

  • ✅ 检查服务状态
  • ✅ 创建和管理会话
  • ✅ 发送消息并获取流式响应
  • ✅ 实时打印 AI 回复内容
  • ✅ 停止正在进行的对话

核心功能设计

1. 客户端初始化

python 复制代码
class CoPawClient:
    def __init__(self, host: str = "127.0.0.1", port: int = 8088):
        self.base_url = f"http://{host}:{port}/api"
        self.session = requests.Session()
        self.session.headers["x-agent-id"] = TO_AGENT
        self.host = host
        self.port = port

关键点:

  • 使用 requests.Session() 保持连接,提高性能
  • 设置 x-agent-id 请求头,标识目标 Agent
  • 默认连接本地 8088 端口

2. 服务健康检查

python 复制代码
def check_server(self) -> bool:
    try:
        response = self.session.get(f"{self.base_url}/chats", timeout=5)
        return response.status_code == 200
    except requests.exceptions.RequestException:
        return False

在发送请求前先检查服务是否可用,避免无谓的等待。

3. 流式响应的核心实现

这是本客户端的核心亮点------真正的流式迭代器:

python 复制代码
def _stream_sse(self, response) -> Iterator[Dict[str, Any]]:
    """
    【真正的流式迭代器】
    每收到一段数据就立即返回,实现实时输出
    """
    for line in response.iter_lines(decode_unicode=True):
        if not line:
            continue
        
        line = line.strip()
        if line.startswith("data: "):
            data_str = line[6:]
            if data_str.strip() == "[DONE]":
                break
            
            try:
                data = json.loads(data_str)
                if data.get("type") == "message" and data.get("content"):
                    yield data  # 实时返回每一段数据
            except json.JSONDecodeError:
                continue

技术要点:

  • 使用 SSE(Server-Sent Events)协议接收流式数据
  • iter_lines(decode_unicode=True) 逐行读取响应
  • yield 关键字实现生成器,边接收边返回
  • 解析 data: 前缀的 SSE 数据格式

4. 发送消息接口

python 复制代码
def send_message(
    self,
    channel: str,
    user_id: str,
    session_id: str,
    content: str,
    stream: bool = True,
) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:
    response = self.session.post(
        f"{self.base_url}/console/chat",
        json={
            "channel": channel,
            "user_id": user_id,
            "session_id": session_id,
            "input": [
                {"role": "user", "content": [{"type": "text", "text": content}]}
            ],
            "stream": stream,
        },
        stream=stream,
        timeout=1200,  # 1200 秒超时=12 分钟
    )
    response.raise_for_status()

    if stream:
        return self._stream_sse(response)  # 返回生成器
    else:
        return self._parse_sse_response(response.text)

设计亮点:

  • 支持流式和非流式两种模式
  • 长超时设置(12 分钟),适应复杂任务
  • 统一接口,灵活切换

5. 实时打印示例

python 复制代码
# 设置 UTF-8 编码,避免 emoji 等特殊字符编码错误
import sys
sys.stdout.reconfigure(encoding='utf-8')

# 流式示例(实时输出)
print("AI 回复:", end="", flush=True)
full_text = ""

# 迭代获取实时流数据
for chunk in client.send_message_by_session(
    user_id=USER_ID,
    channel=CHANNEL,
    session_id=SESSION_ID,
    content=content,
    stream=True
):
    # 提取文本
    for content_item in chunk.get("content", []):
        if content_item.get("type") == "text":
            text = content_item.get("text", "")
            full_text += text
            print(text, end="", flush=True)  # 实时打印!

print("\n\n【接收完成】")

关键技巧:

  • sys.stdout.reconfigure(encoding='utf-8') 设置 UTF-8 输出,避免 emoji 编码错误
  • end="" 避免自动换行
  • flush=True 强制立即输出,不等待缓冲区
  • 累加 full_text 便于后续处理

完整配置参数

python 复制代码
# 对应请求头 x-agent-id: A6T8Kq
TO_AGENT = "fZd52L"
SESSION_ID = "1775560092236"
USER_ID = "default"
COPAW_IP = "127.0.0.1"
COPAW_PORT = 8088
CHANNEL = "console"

参数说明:

参数 说明
TO_AGENT 目标 Agent ID,从 CoPaw 系统获取
SESSION_ID 会话标识,保持对话连续性
USER_ID 用户标识,默认为 "default"
COPAW_IP/PORT CoPaw 服务地址
CHANNEL 通信渠道,console 表示控制台

扩展功能

会话管理

python 复制代码
def create_chat(self, session_id: str, user_id: str, channel: str = "console") -> Dict[str, Any]:
    response = self.session.post(
        f"{self.base_url}/chats",
        json={"session_id": session_id, "user_id": user_id, "channel": channel},
    )
    response.raise_for_status()
    return response.json()

def list_chats(self) -> list:
    response = self.session.get(f"{self.base_url}/chats")
    response.raise_for_status()
    return response.json()

停止对话

python 复制代码
def stop_chat(self, session_id: str) -> Dict[str, Any]:
    list_chats = self.list_chats()
    # 通过 session_id 查找 chat_id
    chat_id = next((chat["chat_id"] for chat in list_chats if chat["session_id"] == session_id), None)
    if chat_id is None:
        raise ValueError(f"会话 ID {session_id} 不存在")

    response = self.session.post(
        f"{self.base_url}/console/chat/stop",
        params={"chat_id": chat_id},
        data=b""
    )
    response.raise_for_status()
    return response.json()

使用场景

  1. CLI 工具开发:构建命令行 AI 助手
  2. 自动化脚本:集成 AI 能力到工作流
  3. 数据处理:批量处理文件并获取 AI 分析
  4. 原型验证:快速测试 Agent 功能

依赖安装

bash 复制代码
pip install requests

常见问题

1. TypeError: unsupported operand type(s) for |

问题: Python 版本低于 3.10 时,| 联合类型语法不支持。

解决: 使用 Union 替代:

python 复制代码
from typing import Union

# 错误(Python < 3.10)
) -> Iterator[Dict[str, Any]] | Dict[str, Any]:

# 正确(兼容所有版本)
) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:

2. UnicodeEncodeError: 'gbk' codec can't encode character

问题: Windows 控制台默认 GBK 编码,无法处理 emoji 等 UTF-8 字符。

解决: 设置控制台输出为 UTF-8:

python 复制代码
import sys
sys.stdout.reconfigure(encoding='utf-8')

总结

本文介绍的 CoPaw Python 客户端具有以下优势:

  • 🚀 真正的流式响应:边接收边输出,用户体验流畅
  • 🔧 灵活配置:支持多种参数定制
  • 📦 完整功能:涵盖会话管理、消息发送、对话停止
  • 🎯 易于集成:简洁的 API 设计,快速上手
  • 兼容性好:支持 Python 3.8+,解决 Windows 编码问题

通过理解 SSE 流式通信和生成器的使用,你可以将这种模式应用到其他 AI 服务的客户端开发中。


附录:完整源码

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
CoPaw Agent Python 客户端(支持流式响应 + 实时打印)

功能特性:
- 支持 SSE 流式响应,实现类似 ChatGPT 的打字机效果
- 支持非流式模式,一次性获取完整回复
- 会话管理:创建、列出、停止会话
- 服务健康检查
- Windows 编码兼容:自动设置 UTF-8 输出

作者:
接口是根据浏览器请求链接获取的,有些不一定准确,也没找到Copaw的对接文档,自行测试吧
日期:2026-04-10
"""

import requests
from typing import Optional, Dict, Any, Iterator, Union
import json


# ==================== 配置参数 ====================
# 对应请求头 x-agent-id: A6T8Kq
# 目标 Agent ID,从 CoPaw 系统获取
TO_AGENT = "fZd52L"

# 会话标识,保持对话连续性
# 同一 session_id 的对话会有上下文记忆
SESSION_ID = "1775560092236"

# 用户标识,默认为 "default"
USER_ID = "default"

# CoPaw 服务地址
COPAW_IP = "127.0.0.1"
COPAW_PORT = 8088

# 通信渠道,console 表示控制台
CHANNEL = "console"


# ==================== CoPawClient 类 ====================
class CoPawClient:
    """
    CoPaw Agent Python 客户端
    
    提供与本地 CoPaw 服务通信的完整接口,
    支持流式和非流式两种响应模式。
    """
    
    def __init__(self, host: str = "127.0.0.1", port: int = 8088):
        """
        初始化客户端
        
        Args:
            host: CoPaw 服务主机地址,默认本地
            port: CoPaw 服务端口,默认 8088
        """
        # 构建 API 基础 URL
        self.base_url = f"http://{host}:{port}/api"
        
        # 创建会话对象,保持连接提高性能
        self.session = requests.Session()
        
        # 设置请求头,标识目标 Agent
        self.session.headers["x-agent-id"] = TO_AGENT
        
        # 保存主机和端口信息
        self.host = host
        self.port = port

    def check_server(self) -> bool:
        """
        检查 CoPaw 服务是否可用
        
        Returns:
            bool: 服务可用返回 True,否则返回 False
        """
        try:
            # 尝试访问 chats 端点
            response = self.session.get(f"{self.base_url}/chats", timeout=5)
            return response.status_code == 200
        except requests.exceptions.RequestException:
            # 网络异常或超时
            return False

    def create_chat(
        self, session_id: str, user_id: str, channel: str = "console"
    ) -> Dict[str, Any]:
        """
        创建新会话
        
        Args:
            session_id: 会话标识
            user_id: 用户标识
            channel: 通信渠道,默认 console
            
        Returns:
            Dict: 创建结果,包含 chat_id 等信息
        """
        response = self.session.post(
            f"{self.base_url}/chats",
            json={"session_id": session_id, "user_id": user_id, "channel": channel},
        )
        response.raise_for_status()
        return response.json()

    def list_chats(self) -> list:
        """
        列出所有会话
        
        Returns:
            list: 会话列表
        """
        response = self.session.get(f"{self.base_url}/chats")
        response.raise_for_status()
        return response.json()

    def send_message(
        self,
        channel: str,
        user_id: str,
        session_id: str,
        content: str,
        stream: bool = True,
    ) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:
        """
        发送消息到 Agent
        
        Args:
            channel: 通信渠道
            user_id: 用户标识
            session_id: 会话标识
            content: 消息内容
            stream: 是否启用流式响应,默认 True
            
        Returns:
            流式模式下返回迭代器,非流式模式下返回完整响应字典
        """
        # 发送 POST 请求到 console/chat 端点
        response = self.session.post(
            f"{self.base_url}/console/chat",
            json={
                "channel": channel,
                "user_id": user_id,
                "session_id": session_id,
                "input": [
                    {"role": "user", "content": [{"type": "text", "text": content}]}
                ],
                "stream": stream,
            },
            stream=stream,  # 启用流式接收
            timeout=1200,   # 1200 秒超时 = 20 分钟,适应复杂任务
        )
        response.raise_for_status()

        # 根据模式返回不同结果
        if stream:
            return self._stream_sse(response)  # 返回生成器
        else:
            return self._parse_sse_response(response.text)

    def _stream_sse(self, response) -> Iterator[Dict[str, Any]]:
        """
        【核心功能】流式 SSE 响应迭代器
        
        逐行读取服务器返回的 SSE 数据,每收到一段就立即 yield,
        实现真正的实时输出效果。
        
        Args:
            response: requests 响应对象
            
        Yields:
            Dict: 解析后的数据块,包含 type 和 content 字段
        """
        # 逐行迭代响应内容
        for line in response.iter_lines(decode_unicode=True):
            # 跳过空行
            if not line:
                continue
            
            line = line.strip()
            # SSE 数据以 "data: " 开头
            if line.startswith("data: "):
                data_str = line[6:]  # 去掉 "data: " 前缀
                
                # 遇到 [DONE] 表示传输结束
                if data_str.strip() == "[DONE]":
                    break
                
                try:
                    # 解析 JSON 数据
                    data = json.loads(data_str)
                    # 只返回 type 为 message 且有 content 的数据
                    if data.get("type") == "message" and data.get("content"):
                        yield data  # 实时返回每一段数据
                except json.JSONDecodeError:
                    # JSON 解析失败则跳过
                    continue

    def _parse_sse_response(self, sse_text: str) -> Dict[str, Any]:
        """
        解析 SSE 响应(非流式模式)
        
        一次性解析完整的 SSE 响应文本,返回结构化结果。
        
        Args:
            sse_text: 完整的 SSE 响应文本
            
        Returns:
            Dict: 包含 messages、last_message 和 text 的字典
        """
        messages = []      # 所有消息列表
        last_message = None  # 最后一条消息
        text_parts = []    # 文本片段列表

        # 逐行解析
        for line in sse_text.strip().split("\n"):
            if line.startswith("data: "):
                data_str = line[6:]
                if data_str.strip() == "[DONE]":
                    break
                try:
                    data = json.loads(data_str)
                    if data.get("type") == "message":
                        messages.append(data)
                        last_message = data
                        # 提取文本内容
                        if data.get("content"):
                            for content_item in data.get("content", []):
                                if content_item.get("type") == "text":
                                    text_parts.append(content_item.get("text", ""))
                except json.JSONDecodeError:
                    continue

        return {
            "messages": messages,
            "last_message": last_message,
            "text": "".join(text_parts),  # 拼接完整文本
        }

    def send_message_by_session(
        self,
        session_id: str,
        content: str,
        user_id: str = "default",
        channel: str = "console",
        stream: bool = True,
    ) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:
        """
        便捷的会话消息发送方法
        
        简化参数,默认使用全局配置的 user_id 和 channel。
        
        Args:
            session_id: 会话标识
            content: 消息内容
            user_id: 用户标识,默认 "default"
            channel: 通信渠道,默认 "console"
            stream: 是否流式,默认 True
            
        Returns:
            同 send_message
        """
        return self.send_message(
            channel=channel,
            user_id=user_id,
            session_id=session_id,
            content=content,
            stream=stream,
        )

    def stop_chat(
        self,
        session_id: str
    ) -> Dict[str, Any]:
        """
        停止正在进行的对话
        
        通过 session_id 查找对应的 chat_id,然后发送停止请求。
        
        Args:
            session_id: 会话标识
            
        Returns:
            Dict: 停止操作结果
            
        Raises:
            ValueError: 会话不存在时抛出
        """
        # 获取所有会话列表
        list_chats = self.list_chats()
        
        # 通过 session_id 查找 chat_id
        chat_id = next(
            (chat["chat_id"] for chat in list_chats if chat["session_id"] == session_id),
            None
        )
        
        # 未找到会话则抛出异常
        if chat_id is None:
            raise ValueError(f"会话 ID {session_id} 不存在")

        # 发送停止请求
        response = self.session.post(
            f"{self.base_url}/console/chat/stop",
            params={"chat_id": chat_id},
            data=b""
        )
        response.raise_for_status()
        return response.json()


# ==================== 主程序入口 ====================
if __name__ == "__main__":
    # 设置控制台输出为 UTF-8,避免 emoji 等特殊字符编码错误
    # Windows 控制台默认 GBK 编码,需要显式设置为 UTF-8
    import sys
    sys.stdout.reconfigure(encoding='utf-8')
    
    # 创建客户端实例
    client = CoPawClient(host=COPAW_IP, port=COPAW_PORT)

    # 检查服务是否可用
    if not client.check_server():
        print("[ERROR] CoPaw API 服务未运行!")
        exit(1)

    # ==================== 流式示例(实时输出) ====================
    print("\n【流式模式 - 实时打印】")

    # 测试消息内容
    content = "你好,测试普通响应"
    
    # 打印提示,不换行
    print("AI 回复:", end="", flush=True)
    full_text = ""  # 用于累加完整回复
    
    # 迭代获取实时流数据
    for chunk in client.send_message_by_session(
        user_id=USER_ID,
        channel=CHANNEL,
        session_id=SESSION_ID,
        content=content,
        stream=True
    ):
        # 提取文本内容
        for content_item in chunk.get("content", []):
            if content_item.get("type") == "text":
                text = content_item.get("text", "")
                full_text += text
                # 实时打印!end="" 避免换行,flush=True 强制立即输出
                print(text, end="", flush=True)
    
    print("\n\n【接收完成】")
    # print(f"完整回复:{full_text}")

    # ==================== 非流式示例 ====================
    # 取消注释可测试非流式模式
    # print("\n【非流式模式】")
    # result = client.send_message_by_session(
    #     session_id=SESSION_ID,
    #     content="你好,测试普通响应",
    #     stream=False
    # )
    # print(f"回复:{result['text']}")

源码关键注释说明

代码段 关键注释 作用
from typing import Union 兼容 Python < 3.10 替代 `
requests.Session() 保持连接 复用 TCP 连接,提高性能
x-agent-id 请求头 标识目标 Agent CoPaw 多 Agent 路由
stream=stream 启用流式接收 配合 SSE 协议实现实时输出
iter_lines() 逐行读取 避免一次性加载大响应
yield data 生成器 边接收边返回,实现流式
sys.stdout.reconfigure(encoding='utf-8') UTF-8 输出 解决 Windows 控制台 emoji 编码问题
end="", flush=True 实时打印 打字机效果的关键
timeout=1200 长超时 适应复杂任务(20 分钟)
params={"chat_id": chat_id} 查询参数 停止会话时传递 chat_id

作者: 文档 99% 由 Copaw 生成
更新时间: 2026-04-10
Python 版本: 3.8+

相关推荐
咬_咬2 小时前
go语言学习(数组与切片)
开发语言·学习·golang·数组·切片
小陈工2 小时前
Python Web开发入门(十八):跨域问题解决方案——从“为什么我的请求被拦了“到“我让浏览器乖乖听话“
开发语言·python·机器学习·架构·数据挖掘·回归·状态模式
m0_497214152 小时前
Qt事件系统
开发语言·qt
AI科技星2 小时前
全维度相对论推导、光速螺旋时空与北斗 GEO 钟差的统一理论
开发语言·线性代数·算法·机器学习·数学建模
赵优秀一一2 小时前
Python 工程化基础1:环境(conda)、pip、requirements.txt
linux·开发语言·python
kaizq2 小时前
Python-Nacos电商订单分布微服系统开发
python·nacos·分布微服务·ai-ima-glm·电商订单
li1670902702 小时前
第十章:list
c语言·开发语言·数据结构·c++·算法·list·visual studio
游乐码2 小时前
C#List
开发语言·c#·list
kishu_iOS&AI2 小时前
机器学习 —— 线性回归(实例)
人工智能·python·机器学习·线性回归