langfuse的坑

1.langfuse不支持分片模式的ck集群

 

2.langfuse默认的CLICKHOUSE_CLUSTER_NAME是default,默认的CLICKHOUSE_DB也是default

如果使用了自定义的集群名和自定义的数据库,需要手动执行up.sh脚本

2.1修改环境变量LANGFUSE_AUTO_CLICKHOUSE_MIGRATION_DISABLED为true

2.2去sql的文件夹做批量替换

cd ./packages/shared/clickhouse/migrations/clustered/
sed -i 's/ON CLUSTER default/ON CLUSTER my_langfuse_cluster/g' *.sql

find . -name "*.sql" -type f -exec sed -i 's/DROP TABLE IF EXISTS \([a-zA-Z0-9_]*\) ON CLUSTER/DROP TABLE IF EXISTS my_langfuse.\1 ON CLUSTER/g' {} \;
find . -name "*.sql" -type f -exec sed -i 's/DROP VIEW IF EXISTS \([a-zA-Z0-9_]*\) ON CLUSTER/DROP VIEW IF EXISTS my_langfuse.\1 ON CLUSTER/g' {} \;

cd /app/packages/shared
sh ./clickhouse/scripts/up.sh

#注意如果ck的密码有特殊字符需要转义

 

3.使用的版本是3.175,有些表是表引擎不是可复制的表引擎,比如event_log表

后面的4.0版本就没有了这个event_log表

 

4.如果要使用多分片的集群

ck配置,新增一个1分片多副本的集群,新增一个用户

           <my_langfuse_cluster>
            <shard>
                 <internal_replication>true</internal_replication>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-1-0.my-clickhouse-1-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-2-0.my-clickhouse-2-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-3-0.my-clickhouse-3-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-4-0.my-clickhouse-4-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-5-0.my-clickhouse-5-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
                 <replica>
                      <default_database>my_langfuse</default_database>
                      <host>my-clickhouse-6-0.my-clickhouse-6-headless</host>
                      <port>9000</port>
                      <user>langfuse</user>
                      <password>123456</password>
                 </replica>
            </shard>
          </my_langfuse_cluster>
         <langfuse>
            <password>123456</password>
            <networks>
                <ip>::/0</ip>
            </networks>

            <!-- Settings profile for user. -->
            <profile>default</profile>

            <!-- Quota for user. -->
            <quota>default</quota>

            <!-- User can create other users and grant rights to them. -->
            <access_management>1</access_management>
        </langfuse>

 

修改langfuse的建表语句

sed -i "s#ReplicatedReplacingMergeTree(event_ts, is_deleted)#ReplicatedReplacingMergeTree('/clickhouse/tables/1/{database}/{table}', '{replica}', event_ts, is_deleted)#g" *.sql
sed -i "s#ReplicatedAggregatingMergeTree#ReplicatedAggregatingMergeTree('/clickhouse/tables/1/{database}/{table}', '{replica}')#g" 0009_add_project_environments.up.sql

 

5.测试脚本

from openai import OpenAI
from langfuse import Langfuse, propagate_attributes, get_client
import tiktoken
import sys

# ====================== 硬编码配置 ======================
LANGFUSE_PUBLIC_KEY = "pk-lf-xxxxxxxxxx"
LANGFUSE_SECRET_KEY = "sk-lf-xxxxxxxxxxxxx"
LANGFUSE_HOST = "http://langfuse-web.www:20011"

LLM_API_KEY = "xxxxxxxxxx"
LLM_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4"
LLM_MODEL = "glm-5.3-flash"
# ========================================================

# 初始化Langfuse
langfuse = Langfuse(
    public_key=LANGFUSE_PUBLIC_KEY,
    secret_key=LANGFUSE_SECRET_KEY,
    host=LANGFUSE_HOST,
    debug=True
)
lf_client = get_client()

# 初始化LLM客户端
llm_client = OpenAI(
    api_key=LLM_API_KEY,
    base_url=LLM_BASE_URL
)

# Token估算工具(兜底用)
def count_message_tokens(messages):
    enc = tiktoken.get_encoding("cl100k_base")
    total = 0
    for msg in messages:
        total += len(enc.encode(msg["content"]))
    return total

def count_text_tokens(text: str):
    enc = tiktoken.get_encoding("cl100k_base")
    return len(enc.encode(text))


def safe_input(prompt: str) -> str:
    """
    替代原生input(),处理乱码/特殊字节,避免UnicodeDecodeError崩溃
    """
    sys.stdout.write(prompt)
    sys.stdout.flush()
    raw_bytes = sys.stdin.buffer.readline()
    if not raw_bytes:
        raise EOFError()
    # 解码容错,遇到非法字节直接替换,不抛异常
    text = raw_bytes.decode("utf‑8", errors="replace")
    # 去掉末尾换行
    return text.rstrip("\r\n")


def chat_round(messages):
    """
    传入完整messages上下文,执行一轮对话,完整埋点上报
    :param messages: list[dict] 完整对话上下文
    :return: answer, (prompt_tok, comp_tok, total_tok)
    """
    user_query = messages[-1]["content"]

    with lf_client.start_as_current_observation(
        as_type="span",
        name="chat-round-workflow",
        input={"user_query": user_query}
    ) as root_span:
        with propagate_attributes(
            user_id="chat-user-001",
            session_id="chat-session-001",
            tags=["chat-demo", "glm", "interactive"]
        ):
            resp = llm_client.chat.completions.create(
                model=LLM_MODEL,
                messages=messages,
                temperature=0.7,
                extra_body={"return_usage": True}
            )
            answer = resp.choices[0].message.content

            # 获取token
            if resp.usage:
                prompt_tok = resp.usage.prompt_tokens
                comp_tok = resp.usage.completion_tokens
                total_tok = resp.usage.total_tokens
            else:
                prompt_tok = count_message_tokens(messages)
                comp_tok = count_text_tokens(answer)
                total_tok = prompt_tok + comp_tok

            # generation埋点,usage_details保证token展示
            with lf_client.start_as_current_observation(
                as_type="generation",
                name="glm-chat-call",
                model=LLM_MODEL,
                model_parameters={"temperature": 0.7},
                input=messages
            ) as gen_obs:
                gen_obs.update(
                    output={"content": answer},
                    usage_details={
                        "prompt_tokens": prompt_tok,
                        "completion_tokens": comp_tok,
                        "total_tokens": total_tok
                    }
                )

            root_span.update(output={"answer": answer})

    lf_client.flush()
    return answer, (prompt_tok, comp_tok, total_tok)


if __name__ == "__main__":
    print("✅ Langfuse交互式对话启动 | 输入exit退出,输入clear清空上下文")
    # 多轮对话上下文
    chat_messages = [
        {"role": "system", "content": "你是AI助手,回答简洁准确"}
    ]

    while True:
        try:
            user_text = safe_input("\n👤 你:")
        except EOFError:
            print("\n⚠️检测到EOF,重新输入问题")
            continue
        except KeyboardInterrupt:
            print("\n👋 用户中断,退出对话")
            break

        cmd = user_text.strip().lower()
        if cmd == "exit":
            print("👋 退出对话")
            break
        if cmd == "clear":
            chat_messages = [{"role": "system", "content": "你是AI助手,回答简洁准确"}]
            print("🧹上下文已清空")
            continue
        if not cmd:
            continue

        # 追加用户消息
        chat_messages.append({"role": "user", "content": user_text})
        ans, tok = chat_round(chat_messages)
        # 追加AI回复到上下文
        chat_messages.append({"role": "assistant", "content": ans})

        print(f"🤖 AI:{ans}")
        print(f"📊 Token统计|输入:{tok[0]} 输出:{tok[1]} 总计:{tok[2]}")

 

posted @ 2026-07-30 17:08  wdgde  阅读(7)  评论(0)    收藏  举报