引言
在前面(通过日志分析华为CangjieMagic智能体框架的工作机理-优快云博客)已经介绍过可以通过日志文件去分析Cangjie Magic智能体的工作过程,今天分享另一种打印内部流程的方式,就是用TagStream和ConsolePrinter。
模型异步执行
TagStream实际上是当Cangjie Magic内部处理大模型输出时,它会把输出结果进行解析,并以流的方式提供给程序。要使用这种方式,需要将模型改为异步执行(即所谓流工作方式)。例如:
let resp = group.asyncChat(AgentRequest("当前血糖 189 mg/dL", verbose: true))
在我的测试中Cangjie Magic智能体的异步方式和华为云的DeepSeek不兼容,因为华为云返回数据的方式和Cangjie Magic预期不同,会发生解析错误。以下的所有测试都是使用的火山引擎的DeepSeek完成的,即model: "ark:deepseek-r1-250528"。
使用ConsolePrinter类进行输出
可以采用ConsolePrinter类来输出中间的日志信息。这个类实际上是
TagStream派生得到的,它将模型输出信息输出到控制台。这个类的定义如下:
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
*/
package magic.agent_executor.common
import magic.parser.*
import magic.core.agent.AsyncAgentResponse
import magic.utils.{Color, Colorful}
import std.console.Console
/**
* Dumper the verbose react internal information for debugging
*/
public class ConsolePrinter <: TagStreamVisitor {
public init(chunks: Iterator<String>) {
super(chunks)
}
public init(asyncResponse: AsyncAgentResponse) {
super(asyncResponse.execution.verboseInfo)
}
private func getTagColor(tag: String): Color {
if (tag == Tag.PLAN) {
return Color.Red
} else if (tag == Tag.INFO) {
return Color.Green
} else {
return Color.Blue
}
}
override protected func onTag(tag: String): Unit {
if (tag == Tag.THOUGHT || tag == Tag.ACTION ||
tag == Tag.OBSERVATION || tag == Tag.INFO || tag == Tag.PLAN) {
let tagColor = getTagColor(tag)
Console.stdOut.writeln(tag.withColor(tagColor))
}
}
override protected func onCloseTag(tag: String): Unit {
let tagColor = getTagColor(tag)
if (tag == Tag.INFO) {
Console.stdOut.writeln(tag.getCloseTag().withColor(tagColor))
Console.stdOut.writeln("---------------------------------")
} else if (tag == Tag.ANSWER) {
Console.stdOut.writeln("...")
} else {
Console.stdOut.writeln(tag.getCloseTag().withColor(tagColor))
}
}
protected func onChunk(chunk: String): Unit {
if (this.currTag != Tag.ANSWER) {
Console.stdOut.write(chunk)
}
}
public func dump(): Unit {
this.start()
}
public static func print(asyncResponse: AsyncAgentResponse, verbose!: Bool = false): Unit {
if (verbose) {
let dumper = ConsolePrinter(asyncResponse)
dumper.dump()
}
for (data in asyncResponse) {
Console.stdOut.write(data)
}
Console.stdOut.write("\n")
}
}
可以修改一下Health Monitor例程:
let resp = group.asyncChat(AgentRequest("当前血糖 189 mg/dL", verbose: true))
ConsolePrinter.print(resp, verbose: true)
得到的输出如下:
[Thought]
The user provided a blood glucose value of 189 mg/dL. I need to determine if this level is normal or abnormal using the CgmAgent tool. [/Thought]
[Action]
```json
{
"name": "CgmAgent",
"arguments": {
"question": "当前血糖 189 mg/dL"
}
}
```
[/Action]
>>>>>> 您的血糖检测结果为189 mg/dL(高于正常范围)。建议:1. 立即饮用500ml水帮助代谢 2. 进行15分钟轻度运动(如散步)3. 30分钟后重新检测血糖 4. 若持续高于180 mg/dL请及时就医 <<<<<
[Observation]
异常[/Observation]
[Thought]
The CgmAgent tool has analyzed the blood glucose level of 189 mg/dL and returned "异常" (abnormal). This indicates the user's current blood sugar is outside the normal range. [/Thought]
...
您的血糖检测结果异常(189 mg/dL)。正常空腹血糖应低于100 mg/dL,餐后血糖应低于140 mg/dL。建议:
1️⃣ 立即停止摄入任何含糖食物/饮料
2️⃣ 若您有糖尿病史,请按医嘱服用降糖药
3️⃣ 30分钟后重新测量血糖
4️⃣ 若持续高于180 mg/dL或出现头晕/恶心症状,请立即就医
⚠️ 此结果仅作参考,具体诊疗请咨询医生
从TagStream派生自定义打印输出
用户也可以直接从TagStream类派生自己的类来定制输出。比如下面的类会打印模型输出到控制台,只要不是最终的回答,都会加上下划线。
class MyPrinter <: TagStreamVisitor {
public init(chunks: Iterator<String>) {
super(chunks)
}
override protected func onTag(tag: String): Unit {
if(tag != "[Answer]"){
Console.stdOut.write("\u{1b}[4m${tag}")
}
}
override protected func onCloseTag(tag: String): Unit {
if(tag != "[Answer]"){
Console.stdOut.writeln("${tag}\u{1b}[0m")
}
}
protected func onChunk(chunk: String): Unit {
Console.stdOut.write("${chunk}")
}
}
然后,使用下面的代码就可以直接输出了。
let agent = BlackCatAssistant()
let result = agent.asyncChat(AgentRequest("一只耳来啦", verbose: true))
let printer = MyPrinter(result.execution.verboseInfo)
printer.start()
for (data in result) {
Console.stdOut.write(data)
}
Console.stdOut.write("\n")
结束语
Cangjie Magic所提供的TagSteam类可以方便优化显示输出,但是其功能没有日志功能全面,也不及LangChain通过的BaseCallbackHandler多。